Browse Source

Merge pull request #3814 from cnAbp/docs

Translate part-3 & modifying menu document
pull/3721/head^2
maliming 6 years ago
committed by GitHub
parent
commit
2cdc617d9b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      docs/en/Tutorials/Part-3.md
  2. 2
      docs/zh-Hans/Tutorials/Part-2.md
  3. 199
      docs/zh-Hans/Tutorials/Part-3.md
  4. 9
      docs/zh-Hans/UI/Angular/Config-State.md
  5. 197
      docs/zh-Hans/UI/Angular/Modifying-the-Menu.md
  6. BIN
      docs/zh-Hans/UI/Angular/images/navigation-menu-after-patching.png
  7. BIN
      docs/zh-Hans/UI/Angular/images/navigation-menu-search-input.png
  8. BIN
      docs/zh-Hans/UI/Angular/images/navigation-menu-via-app-routing.png
  9. BIN
      docs/zh-Hans/UI/Angular/images/navigation-menu-via-config-state.png
  10. 4
      docs/zh-Hans/docs-nav.json

4
docs/en/Tutorials/Part-3.md

@ -96,9 +96,9 @@ namespace Acme.BookStore
````
* `IRepository<Book, Guid>` is injected and used it in the `SeedAsync` to create two book entities as the test data.
* `IGuidGenerator` is injected to create GUIDs. While `Guid.NewGuid()` would perfectly work for testing, `IGuidGenerator` has additional features especially important while using real databases. Further information, see the [Guid generation document](../Guid-Generation.md).
### Testing the application service BookAppService
* `IGuidGenerator` is injected to create GUIDs. While `Guid.NewGuid()` would perfectly work for testing, `IGuidGenerator` has additional features especially important while using real databases. Further information, see the [Guid generation document](https://docs.abp.io/{{Document_Language_Code}}/abp/{{Document_Version}}/Guid-Generation).
### Testing the application service BookAppService
Create a test class named `BookAppService_Tests` in the `Acme.BookStore.Application.Tests` project:

2
docs/zh-Hans/Tutorials/Part-2.md

@ -26,7 +26,7 @@ end
这是ASP.NET Core{{UI_Value}}系列教程的第二章. 共有三章:
- [Part-1: 创建项目和书籍列表页面](Part-1.md)
- **Part II: 创建,编辑,删除书籍(本章)**
- **Part 2: 创建,编辑,删除书籍(本章)**
- [Part-3: 集成测试](Part-3.md)
> 你也可以观看由ABP社区成员为本教程录制的[视频课程](https://amazingsolutions.teachable.com/p/lets-build-the-bookstore-application).

199
docs/zh-Hans/Tutorials/Part-3.md

@ -1 +1,198 @@
TODO ....
## ASP.NET Core {{UI_Value}} 教程 - 第三章
````json
//[doc-params]
{
"UI": ["MVC","NG"]
}
````
{{
if UI == "MVC"
DB="ef"
DB_Text="Entity Framework Core"
UI_Text="mvc"
else if UI == "NG"
DB="mongodb"
DB_Text="MongoDB"
UI_Text="angular"
else
DB ="?"
UI_Text="?"
end
}}
### 关于本教程
这是ASP.NET Core{{UI_Value}}系列教程的第二章. 共有三章:
- [Part-1: 创建项目和书籍列表页面](Part-1.md)
- [Part 2: 创建,编辑,删除书籍](Part-2.md)
- **Part-3: 集成测试(本章)**
> 你也可以观看由ABP社区成员为本教程录制的[视频课程](https://amazingsolutions.teachable.com/p/lets-build-the-bookstore-application).
### 解决方案中的测试项目
解决方案中有多个测试项目:
![bookstore-test-projects-v2](./images/bookstore-test-projects-{{UI_Text}}.png)
每个项目用于测试相关的应用程序项目.测试项目使用以下库进行测试:
* [xunit](https://xunit.github.io/) 作为主测试框架.
* [Shoudly](http://shouldly.readthedocs.io/en/latest/) 作为断言库.
* [NSubstitute](http://nsubstitute.github.io/) 作为模拟库.
### 添加测试数据
启动模板包含`Acme.BookStore.TestBase`项目中的`BookStoreTestDataSeedContributor`类,它创建一些数据来运行测试.
更改`BookStoreTestDataSeedContributor`类如下所示:
````csharp
using System;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Guids;
namespace Acme.BookStore
{
public class BookStoreTestDataSeedContributor
: IDataSeedContributor, ITransientDependency
{
private readonly IRepository<Book, Guid> _bookRepository;
private readonly IGuidGenerator _guidGenerator;
public BookStoreTestDataSeedContributor(
IRepository<Book, Guid> bookRepository,
IGuidGenerator guidGenerator)
{
_bookRepository = bookRepository;
_guidGenerator = guidGenerator;
}
public async Task SeedAsync(DataSeedContext context)
{
await _bookRepository.InsertAsync(
new Book(id: _guidGenerator.Create(),
name: "Test book 1",
type: BookType.Fantastic,
publishDate: new DateTime(2015, 05, 24),
price: 21
)
);
await _bookRepository.InsertAsync(
new Book(id: _guidGenerator.Create(),
name: "Test book 2",
type: BookType.Science,
publishDate: new DateTime(2014, 02, 11),
price: 15
)
);
}
}
}
````
* 注入`IRepository<Book,Guid>`并在`SeedAsync`中使用它来创建两个书实体作为测试数据.
* 使用`IGuidGenerator`服务创建GUID. 虽然`Guid.NewGuid()`非常适合测试,但`IGuidGenerator`在使用真实数据库时还有其他特别重要的功能(参见[Guid生成文档](../Guid-Generation.md)了解更多信息).
### 测试 BookAppService
`Acme.BookStore.Application.Tests` 项目中创建一个名叫 `BookAppService_Tests` 的测试类:
````csharp
using System;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Validation;
using Microsoft.EntityFrameworkCore.Internal;
namespace Acme.BookStore
{
public class BookAppService_Tests : BookStoreApplicationTestBase
{
private readonly IBookAppService _bookAppService;
public BookAppService_Tests()
{
_bookAppService = GetRequiredService<IBookAppService>();
}
[Fact]
public async Task Should_Get_List_Of_Books()
{
//Act
var result = await _bookAppService.GetListAsync(
new PagedAndSortedResultRequestDto()
);
//Assert
result.TotalCount.ShouldBeGreaterThan(0);
result.Items.ShouldContain(b => b.Name == "Test book 1");
}
}
}
````
* 测试方法 `Should_Get_List_Of_Books` 直接使用 `BookAppService.GetListAsync` 方法来获取用户列表,并执行检查.
新增测试方法,用以测试创建一个合法book实体的场景:
````C#
[Fact]
public async Task Should_Create_A_Valid_Book()
{
//Act
var result = await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "New test book 42",
Price = 10,
PublishDate = DateTime.Now,
Type = BookType.ScienceFiction
}
);
//Assert
result.Id.ShouldNotBe(Guid.Empty);
result.Name.ShouldBe("New test book 42");
}
````
新增测试方法,用以测试创建一个非法book实体失败的场景:
````csharp
[Fact]
public async Task Should_Not_Create_A_Book_Without_Name()
{
var exception = await Assert.ThrowsAsync<Volo.Abp.Validation.AbpValidationException>(async () =>
{
await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "",
Price = 10,
PublishDate = DateTime.Now,
Type = BookType.ScienceFiction
}
);
});
exception.ValidationErrors
.ShouldContain(err => err.MemberNames.Any(mem => mem == "Name"));
}
````
* 由于 `Name` 是空值, ABP 抛出一个 `AbpValidationException` 异常.
打开**测试资源管理器**(测试 -> Windows -> 测试资源管理器)并**执行**所有测试:
![bookstore-appservice-tests](./images/bookstore-appservice-tests.png)
恭喜, 绿色图标表示测试已成功通过!

9
docs/zh-Hans/UI/Angular/Config-State.md

@ -236,7 +236,7 @@ const newRoute: ABP.Route = {
path: "page",
invisible: false,
order: 2,
requiredPolicy: "MyProjectName::MyNewPage"
requiredPolicy: "MyProjectName.MyNewPage"
};
this.config.dispatchAddRoute(newRoute);
@ -248,16 +248,17 @@ this.config.dispatchAddRoute(newRoute);
如果你想要**添加一个子路由,你可以这样做:**
```js
import { eIdentityRouteNames } from '@abp/ng.identity';
// this.config is instance of ConfigStateService
const newRoute: ABP.Route = {
parentName: "AbpAccount::Login",
parentName: eIdentityRouteNames.IdentityManagement,
name: "My New Page",
iconClass: "fa fa-dashboard",
path: "page",
invisible: false,
order: 2,
requiredPolicy: "MyProjectName::MyNewPage"
requiredPolicy: "MyProjectName.MyNewPage"
};
this.config.dispatchAddRoute(newRoute);
@ -291,4 +292,4 @@ this.config.dispatchSetEnvironment({
## 下一步是什么?
* [组件替换](./Component-Replacement.md)
- [修改菜单](./Modifying-the-Menu.md)

197
docs/zh-Hans/UI/Angular/Modifying-the-Menu.md

@ -0,0 +1,197 @@
# 修改菜单
菜单在 @abp/ng.theme.basic包 `ApplicationLayoutComponent` 内部. 有几种修改菜单的方法,本文档介绍了这些方法. 如果你想完全替换菜单,请参考[组件替换文档]了解如何替换布局.
<!-- TODO: Replace layout replacement document with component replacement. Layout replacement document will be created.-->
## 如何添加Logo
环境变量中的 `logoUrl` 是logo的url.
你可以在 `src/assets` 文件夹下添加logo并设置 `logoUrl`:
```js
export const environment = {
// other configurations
application: {
name: 'MyProjectName',
logoUrl: 'assets/logo.png',
},
// other configurations
};
```
## 如何添加导航元素
### 通过 AppRoutingModule 中的 `routes` 属性
你可以通过在 `app-routing.module` 中将路由作为子属性添加到路由配置的 `data` 属性来定义路由. `@abp/ng.core` 包组织路由并将其存储在 `ConfigState` 中.`ApplicationLayoutComponent` 从存储中获取路由显示在菜单上.
你可以像以下一样添加 `routes` 属性:
```js
{
path: 'your-path',
data: {
routes: {
name: 'Your navigation',
order: 3,
iconClass: 'fas fa-question-circle',
requiredPolicy: 'permission key here',
children: [
{
path: 'child',
name: 'Your child navigation',
order: 1,
requiredPolicy: 'permission key here',
},
],
} as ABP.Route, // can be imported from @abp/ng.core
}
}
```
- `name` 是导航元素的标签,可以传递本地化密钥或本地化对象.
- `order` 排序导航元素.
- `iconClass``i` 标签的类,在导航标签的左侧.
- `requiredPolicy` 是访问页面所需的权限key. 参阅 [权限管理文档](./Permission-Management.md)
- `children` is an array and is used for declaring child navigation elements. The child navigation element will be placed as a child route which will be available at `'/your-path/child'` based on the given `path` property.
- `children` 是一个数组,用于声明子菜单,它基于给定的 `path` 属性,路径是在`/your-path/child`.
添加了上面描述的route属性后,导航菜单如下图所示:
![navigation-menu-via-app-routing](./images/navigation-menu-via-app-routing.png)
## 通过 ConfigState
`ConfigStateService``dispatchAddRoute` 方法可以向菜单添加一个新的导航元素.
```js
// this.config is instance of ConfigStateService
const newRoute: ABP.Route = {
name: 'My New Page',
iconClass: 'fa fa-dashboard',
path: 'page',
invisible: false,
order: 2,
requiredPolicy: 'MyProjectName.MyNewPage',
} as Omit<ABP.Route, 'children'>;
this.config.dispatchAddRoute(newRoute);
// returns a state stream which emits after dispatch action is complete
```
`newRoute` 放在根级别,没有任何父路由,url将为`/path`.
如果你想 **添加子路由, 你可以这样做:**
```js
// this.config is instance of ConfigStateService
// eIdentityRouteNames enum can be imported from @abp/ng.identity
const newRoute: ABP.Route = {
parentName: eIdentityRouteNames.IdentityManagement,
name: 'My New Page',
iconClass: 'fa fa-dashboard',
path: 'page',
invisible: false,
order: 3,
requiredPolicy: 'MyProjectName.MyNewPage'
} as Omit<ABP.Route, 'children'>;
this.config.dispatchAddRoute(newRoute);
// returns a state stream which emits after dispatch action is complete
```
`newRoute` 做为 `eIdentityRouteNames.IdentityManagement` 的子路由添加, url 设置为 `'/identity/page'`.
新路由看起来像这样:
![navigation-menu-via-config-state](./images/navigation-menu-via-config-state.png)
## 如何修改一个导航元素
`DispatchPatchRouteByName` 方法通过名称查找路由,并使用二个参数传递的新配置替换存储中的配置.
```js
// this.config is instance of ConfigStateService
// eIdentityRouteNames enum can be imported from @abp/ng.identity
const newRouteConfig: Partial<ABP.Route> = {
iconClass: 'fas fa-home',
parentName: eIdentityRouteNames.Administration,
order: 0,
children: [
{
name: 'Dashboard',
path: 'dashboard',
},
],
};
this.config.dispatchPatchRouteByName('::Menu:Home', newRouteConfig);
// returns a state stream which emits after dispatch action is complete
```
* 根据给定的 `parentName`_Home_ 导航移动到 _Administration_ 下拉框下.
* 添加了 icon.
* 指定了顺序.
* 添加了名为 _Dashboard_ 的子路由.
修改后,导航元素看起来像这样:
![navigation-menu-after-patching](./images/navigation-menu-after-patching.png)
## 如何在菜单的右侧添加元素
右侧的元素存储在 @abp/ng.theme.basic 包的 `LayoutState` 中.
`LayoutStateService``dispatchAddNavigationElement` 方法添加元素到右侧的菜单.
你可以通过将模板添加到 `app.component` 调用 `dispatchAddNavigationElement` 方法来插入元素:
```js
import { Layout, LayoutStateService } from '@abp/ng.theme.basic'; // added this line
@Component({
selector: 'app-root',
template: `
<!-- Added below content -->
<ng-template #search
><input type="search" placeholder="Search" class="bg-transparent border-0"
/></ng-template>
`,
})
export class AppComponent {
// Added ViewChild
@ViewChild('search', { static: false, read: TemplateRef }) searchElementRef: TemplateRef<any>;
constructor(private layout: LayoutStateService) {} // injected LayoutStateService
// Added ngAfterViewInit
ngAfterViewInit() {
const newElement = {
name: 'Search',
element: this.searchElementRef,
order: 1,
} as Layout.NavigationElement;
this.layout.dispatchAddNavigationElement(newElement);
}
}
```
上面我们在菜单添加了一个搜索输入,最终UI如下:s
![navigation-menu-search-input](./images/navigation-menu-search-input.png)
## 如何删除右侧菜单元素
TODO
## 下一步是什么?
* [组件替换](./Component-Replacement.md)

BIN
docs/zh-Hans/UI/Angular/images/navigation-menu-after-patching.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
docs/zh-Hans/UI/Angular/images/navigation-menu-search-input.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
docs/zh-Hans/UI/Angular/images/navigation-menu-via-app-routing.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/zh-Hans/UI/Angular/images/navigation-menu-via-config-state.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

4
docs/zh-Hans/docs-nav.json

@ -333,6 +333,10 @@
"text": "配置状态",
"path": "UI/Angular/Config-State.md"
},
{
"text": "修改菜单",
"path": "UI/Angular/Modifying-the-Menu.md"
},
{
"text": "替换组件",
"path": "UI/Angular/Component-Replacement.md"

Loading…
Cancel
Save