|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 44 KiB |
@ -1,3 +1,8 @@
|
||||
## Getting Started With the Angular Application Template
|
||||
# 启动模板入门
|
||||
|
||||
TODO...
|
||||
请参阅以下教程,了解如何使用预构建的应用程序启动模板开始使用ABP框架:
|
||||
|
||||
* [ASP.NET Core MVC / Razor Pages UI 入门](Getting-Started?UI=MVC&DB=EF&Tiered=No)
|
||||
* [Angular UI 入门](Getting-Started?UI=NG&DB=EF&Tiered=No)
|
||||
|
||||
<!-- TODO: this document has been moved, it should be deleted in the future. -->
|
||||
@ -1,102 +1,8 @@
|
||||
## ASP.NET Core MVC 模板入门
|
||||
# 启动模板入门
|
||||
|
||||
### 创建新项目
|
||||
请参阅以下教程,了解如何使用预构建的应用程序启动模板开始使用ABP框架:
|
||||
|
||||
本教程使用 **ABP CLI** 创建一个新项目. 更多选项, 请参阅[入门](https://abp.io/get-started)页面.
|
||||
* [ASP.NET Core MVC / Razor Pages UI 入门](Getting-Started?UI=MVC&DB=EF&Tiered=No)
|
||||
* [Angular UI 入门](Getting-Started?UI=NG&DB=EF&Tiered=No)
|
||||
|
||||
如果你之前未安装,请使用命令行安装ABP CLI:
|
||||
|
||||
````bash
|
||||
dotnet tool install -g Volo.Abp.Cli
|
||||
````
|
||||
|
||||
在空文件夹中使用 `abp new` 命令来创建项目:
|
||||
|
||||
````bash
|
||||
abp new Acme.BookStore
|
||||
````
|
||||
|
||||
> 你可以使用不同级别的命名空间; 例如BookStore, Acme.BookStore或Acme.Retail.BookStore.
|
||||
|
||||
`new` 命令创建**分层MVC应用程序**, **Entity Framework Core**作为数据库提供程序. 但是,它还有其他选择. 有关所有可用选项,请参见[CLI文档](CLI.md)
|
||||
|
||||
#### 预先要求
|
||||
|
||||
创建项目的要求:
|
||||
|
||||
* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/)
|
||||
* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/)
|
||||
* [Node v12+](https://nodejs.org)
|
||||
* [Yarn v1.19+](https://classic.yarnpkg.com/)
|
||||
|
||||
### 解决方案结构
|
||||
|
||||
在**Visual Studio**中打开解决方案:
|
||||
|
||||

|
||||
|
||||
该解决方案具有分层结构(基于[Domain Driven Design](Domain-Driven-Design.md)), 并包含配置好的的单元&集成测试项目,可与**EF Core**和**SQLite**数据库内存一起使用.
|
||||
|
||||
> 请参阅[应用程序模板文档](Startup-Templates/Application.md)以详细了解解决方案结构.
|
||||
|
||||
### 数据库连接字符串
|
||||
|
||||
查看`.Web`项目下`appsettings.json`文件中的 **连接字符串**:
|
||||
|
||||
````json
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"Default": "Server=localhost;Database=BookStore;Trusted_Connection=True"
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
解决方案使用 **Entity Framework Core** 和 **MS SQL Server**. EF Core支持[各种](https://docs.microsoft.com/zh-cn/ef/core/providers/)数据库提供程序,因此你可以根据实际需要使用其他DBMS. 如果需要,请更改连接字符串.
|
||||
|
||||
### 创建数据库并应用数据库迁移
|
||||
|
||||
你有两个选项来创建数据库.
|
||||
|
||||
#### 使用DbMigrator应用程序
|
||||
|
||||
该解决方案包含一个控制台应用程序(在此示例中名为`Acme.BookStore.DbMigrator`),可以创建数据库,应用迁移和初始化数据. 它对开发和生产环境都很有用.
|
||||
|
||||
> `.DbMigrator`项目有自己的`appsettings.json`. 因此,如果你更改了上面的连接字符串,则还应更改此字符串.
|
||||
|
||||
右键单击`.DbMigrator`项目并选择 **设置为启动项目**:
|
||||
|
||||

|
||||
|
||||
按F5(或Ctrl + F5)运行应用程序. 它将具有如下所示的输出:
|
||||
|
||||

|
||||
|
||||
#### 使用EF Core Update-Database命令
|
||||
|
||||
Ef Core具有`Update-Database`命令, 可根据需要创建数据库并应用挂起的迁移. 右键单击`.Web`项目并选择**设置为启动项目**:
|
||||
|
||||

|
||||
|
||||
打开**包管理器控制台(Package Manager Console)**, 选择`.EntityFrameworkCore.DbMigrations`项目作为**默认项目**并运行`Update-Database`命令:
|
||||
|
||||

|
||||
|
||||
这将基于配置的连接字符串创建新数据库.
|
||||
|
||||
> 使用`.Migrator`工具是建议的方法, 因为它还能初始化初始数据能够正确运行Web应用程序.
|
||||
|
||||
### 运行应用程序
|
||||
|
||||
你现在可以运行应用程序,它将会打开**home**页面:
|
||||
|
||||

|
||||
|
||||
单击 **登录** 按钮, 输入用户名`admin`, 密码`1q2w3E*`, 登录应用程序.
|
||||
|
||||
启动模板包括**身份管理**和**租户管理**模块. 登录后,将显示"管理"菜单, 你可以在其中管理**租户**,**角色**,**用户**和**权限**. 用户管理页面如下所示:
|
||||
|
||||

|
||||
|
||||
### 下一步是什么?
|
||||
|
||||
* [应用程序开发教程](Tutorials/AspNetCore-Mvc/Part-I.md)
|
||||
<!-- TODO: this document has been moved, it should be deleted in the future. -->
|
||||
@ -1,6 +1,8 @@
|
||||
# 启动模板入门
|
||||
|
||||
参阅下面的教程来学习如何开始使用的ABP框架预构建的应用程序启动模板:
|
||||
请参阅以下教程,了解如何使用预构建的应用程序启动模板开始使用ABP框架:
|
||||
|
||||
* [ASP.NET Core MVC/Razor页面模板入门](Getting-Started-AspNetCore-MVC-Template.md)
|
||||
* [Angular UI模板入门](Getting-Started-Angular-Template.md)
|
||||
* [ASP.NET Core MVC / Razor Pages UI 入门](Getting-Started?UI=MVC&DB=EF&Tiered=No)
|
||||
* [Angular UI 入门](Getting-Started?UI=NG&DB=EF&Tiered=No)
|
||||
|
||||
<!-- TODO: this document has been moved, it should be deleted in the future. -->
|
||||
@ -0,0 +1,416 @@
|
||||
## 入门
|
||||
|
||||
````json
|
||||
//[doc-params]
|
||||
{
|
||||
"UI": ["MVC","NG"],
|
||||
"DB": ["EF", "Mongo"],
|
||||
"Tiered": ["Yes", "No"]
|
||||
}
|
||||
````
|
||||
|
||||
本教程介绍了如何创建一个新的{{if UI == "MVC"}} ASP.NET Core MVC web {{else if UI == "NG"}} Angular {{end}}. 配置并运行它.
|
||||
|
||||
## 设置你的开发环境
|
||||
|
||||
创建第一个项目之前,需要正确的设置你的开发环境.
|
||||
|
||||
### 预先要求
|
||||
|
||||
你需要安装以下工具:
|
||||
|
||||
* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/) for Windows / [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).
|
||||
* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/)
|
||||
|
||||
* [Node v12+](https://nodejs.org)
|
||||
* [Yarn v1.19+](https://classic.yarnpkg.com/)
|
||||
{{ if Tiered == "Yes" }}
|
||||
|
||||
* [Redis](https://redis.io/): 应用程序将Redis用作[分布式缓存](../Caching.md). 因此你需要安装并运行Redis.
|
||||
|
||||
{{ end }}
|
||||
|
||||
> 你可以也使用其他支持.NET Core 和 ASP.NET Core的编辑器.
|
||||
|
||||
### 安装ABP CLI
|
||||
|
||||
[ABP CLI](./CLI.md)是一个命令行页面,用于为基于ABP的应用程序验证和自动化一些任务.
|
||||
|
||||
> ABP CLI是[ABP框架](https://abp.io/)一个免费开源的工具.
|
||||
|
||||
你需要使用以下命令安排ABP CLI:
|
||||
|
||||
````shell
|
||||
dotnet tool install -g Volo.Abp.Cli
|
||||
````
|
||||
|
||||
如果你已经安装,你可以使用以下命令更新到最新版本:
|
||||
|
||||
````shell
|
||||
dotnet tool update -g Volo.Abp.Cli
|
||||
````
|
||||
|
||||
## 创建新项目
|
||||
|
||||
> 本文假设你使用 **{{ UI_Value }}** 做为UI框架 **{{ DB_Value }}** 做为数据库提供程序,对于其它选项,你可以更改文档顶部的首选项.
|
||||
|
||||
### 使用ABP CLI创建一个新项目
|
||||
|
||||
使用ABP CLI的 `new` 命令创建新项目:
|
||||
|
||||
````shell
|
||||
abp new Acme.BookStore -t app{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes" && UI != "NG"}} --tiered {{else if Tiered == "Yes" && UI == "NG"}}--separate-identity-server{{end}}
|
||||
````
|
||||
|
||||
* `-t` 参数指定 [启动模板](Startup-Templates/Application.md) 名称. `app` 是一个启动模板名称,包含了预安装并且配置好的[ABP模块](Modules/Index.md).
|
||||
|
||||
{{ if UI == "NG" }}
|
||||
|
||||
* `-u` 指定UI框架, 本例中是 `angular`.
|
||||
|
||||
{{ if Tiered == "Yes" }}
|
||||
|
||||
* `--separate-identity-server` 参数用于将Identity服务器应用程序与API主机应用程序分隔开. 如果未指定,你将只有一个端点.
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ if DB == "Mongo" }}
|
||||
|
||||
* `-d` 指定数据库提供程序, 本例中是 `mongodb`.
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ if Tiered == "Yes" && UI != "NG" }}
|
||||
|
||||
* `--tiered` 参数用于创建n层解决方案,其中身份验证服务器层,UI层和API层在物理上是分离的.
|
||||
|
||||
{{ end }}
|
||||
|
||||
> 你可以使用不同级别的命令空间; 例如. BookStore, Acme.BookStore or Acme.Retail.BookStore.
|
||||
|
||||
## 解决方案结构
|
||||
|
||||
{{ if UI == "MVC" }}
|
||||
|
||||
创建项目后你会有以下解决方案目录和文件:
|
||||
|
||||

|
||||
|
||||
在Visual Studio中打开 `.sln` 文件时,将看到以下解决方案结构:
|
||||
|
||||
{{if DB == "Mongo"}}
|
||||
|
||||

|
||||
|
||||
{{else}}
|
||||
|
||||

|
||||
|
||||
{{end}}
|
||||
|
||||
{{ else if UI == "NG" }}
|
||||
在创建的解决方案中有三个文件夹:
|
||||
|
||||

|
||||
|
||||
* `angular` 文件夹包含Angular UI应用程序.
|
||||
* `aspnet-core` 文件夹包含后端应用程序.
|
||||
* `react-native` 文件夹包含React Native UI 应用程序.
|
||||
|
||||
打开 `aspnet-core` 文件夹下的 `.sln`(`Visual Studio`解决方案)文件:
|
||||

|
||||
|
||||
{{ end }}
|
||||
|
||||
> ###### 关于解决方案中的项目
|
||||
>
|
||||
> 根据你的**UI**,**数据库**和其他选项,你的解决方案的结构可能略有不同.
|
||||
|
||||
该解决方案具有分层结构(基于[Domain Driven Design](Domain-Driven-Design.md)), 并包含配置好的的单元&集成测试项目.
|
||||
|
||||
{{ if DB == "EF" }}
|
||||
|
||||
集成测试项目已配置为可与 **EF Core** & **SQLite 内存** database同时使用.
|
||||
|
||||
{{ else if DB == "Mongo" }}
|
||||
|
||||
集成测试项目已配置为每个测试创建的内存中的**MongoDB**数据库(使用的[Mongo2Go](https://github.com/Mongo2Go/Mongo2Go)库).
|
||||
|
||||
{{ end }}
|
||||
|
||||
> 请参阅[应用程序模板文档](Startup-Templates/Application.md)详细了解解决方案结构.
|
||||
|
||||
## 创建数据库
|
||||
|
||||
### 数据库连接字符串
|
||||
|
||||
检查 {{if UI == "MVC"}}{{if Tiered == "Yes"}}`.IdentityServer` 和 `.HttpApi.Host` 项目{{else}}`.Web` 项目{{end}}{{else if UI == "NG" }}`.HttpApi.Host` 项目{{end}}下 `appsettings.json` 文件中的 **链接字符串**:
|
||||
|
||||
{{ if DB == "EF" }}
|
||||
|
||||
````json
|
||||
"ConnectionStrings": {
|
||||
"Default": "Server=localhost;Database=BookStore;Trusted_Connection=True"
|
||||
}
|
||||
````
|
||||
|
||||
该解决方案配置为**Entity Framework Core**与**MS SQL Server**一起使用. EF Core支持[各种](https://docs.microsoft.com/en-us/ef/core/providers/)数据库提供程序,因此你可以使用任何受支持的DBMS. 请参阅[Entity Framework集成文档](https://docs.abp.io/en/abp/latest/Entity-Framework-Core)了解如何切换到另一个DBMS.
|
||||
|
||||
### 数据库连接字符串
|
||||
|
||||
查看`.Web`项目下`appsettings.json`文件中的 **连接字符串**:
|
||||
|
||||
````json
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"Default": "Server=localhost;Database=BookStore;Trusted_Connection=True"
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
解决方案使用 **Entity Framework Core** 和 **MS SQL Server**. EF Core支持[各种](https://docs.microsoft.com/zh-cn/ef/core/providers/)数据库提供程序,因此你可以根据实际需要使用其他DBMS. 如果需要,请更改连接字符串.
|
||||
|
||||
### 应用迁移
|
||||
|
||||
该解决方案使用[Entity Framework Core Code First 迁移](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli). 你需要应用迁移来创建数据库,有两种方法迁移数据库.
|
||||
|
||||
#### 使用DbMigrator应用程序应用迁移
|
||||
|
||||
该解决方案包含一个控制台应用程序(在此示例中名为`Acme.BookStore.DbMigrator`),可以创建数据库,应用迁移和初始化数据. 它对开发和生产环境都很有用.
|
||||
|
||||
> `.DbMigrator`项目有自己的`appsettings.json`. 因此,如果你更改了上面的连接字符串,则还应更改此字符串.
|
||||
|
||||
右键单击`.DbMigrator`项目并选择 **设置为启动项目**:
|
||||
|
||||

|
||||
|
||||
按F5(或Ctrl + F5)运行应用程序. 它将具有如下所示的输出:
|
||||
|
||||

|
||||
|
||||
#### 使用EF Core Update-Database命令
|
||||
|
||||
Ef Core具有`Update-Database`命令, 可根据需要创建数据库并应用挂起的迁移. 右键单击`.Web`项目并选择**设置为启动项目**:
|
||||
|
||||
{{ if UI == "MVC" }}
|
||||
|
||||
右键单击{{if Tiered == "Yes"}}`.IdentityServer`{{else}}`.Web`{{end}}项目并选择**设置为启动项目**:
|
||||
|
||||
{{ else if UI != "MVC" }}
|
||||
|
||||
右键单击`.HttpApi.Host`项目并选择**设置为启动项目**:
|
||||
|
||||
{{ end }}
|
||||
|
||||

|
||||
|
||||
打开**包管理器控制台(Package Manager Console)**, 选择`.EntityFrameworkCore.DbMigrations`项目作为**默认项目**并运行`Update-Database`命令:
|
||||
|
||||

|
||||
|
||||
这将基于配置的连接字符串创建新数据库.
|
||||
|
||||
> 使用`.Migrator`工具是建议的方法, 因为它还能初始化初始数据能够正确运行Web应用程序.
|
||||
|
||||
{{ else if DB == "Mongo" }}
|
||||
|
||||
````json
|
||||
"ConnectionStrings": {
|
||||
"Default": "mongodb://localhost:27017/BookStore"
|
||||
}
|
||||
````
|
||||
|
||||
该解决方案被配置为在你的本地计算机中使用 **MongoDB**,因此你需要启动并运行一个MongoDB服务器实例或者将连接字符串更改为另一个MongoDB服务器.
|
||||
|
||||
### 初始化种子数据
|
||||
|
||||
该解决方案附带一个 `.DbMigrator` 控制台应用程序,该应用程序为初始数据提供了种子. 它对于开发以及生产环境都很有用.
|
||||
|
||||
> `.DbMigrator` 项目有自己的 `appsettings.json`.如果你更改了其他项目的 `appsettings.json`,也应该更改这个.
|
||||
|
||||
右键点击 `.DbMigrator` 并选择 **设置为启动项目**.
|
||||
|
||||

|
||||
|
||||
按F5(或Ctrl+F5)启动应用程序,你会看到以下输出:
|
||||
|
||||

|
||||
|
||||
> 数据库创建后会初始化种子数据, 其中包含用于登录的 `admin` 用户. 所以你至少使用 `.DbMigrator` 一次.
|
||||
|
||||
{{ end }}
|
||||
|
||||
### 运行应用程序
|
||||
|
||||
{{ if UI == "MVC" }}
|
||||
|
||||
{{ if Tiered == "Yes" }}
|
||||
|
||||
确保 `.IdentityServer` 是启动项目,运行应用程序后会在你的浏览器打开一个 **login** 页面.
|
||||
|
||||
> 在Visual Studio中使用Ctrl+F5(而不是F5)运行应用,如果你不用于调试,这会减少启动时间.
|
||||
|
||||
你可以登录,但是不能在这里进入主应用程序,它仅是验证服务器.
|
||||
|
||||
确保 `.HttpApi.Host` 是启动项目,运行应用程序后会在你的浏览器打开一个 **Swagger UI** 页面.
|
||||
|
||||

|
||||
|
||||
这里是Web应用程序使用的API应用程序.
|
||||
|
||||
最后确保 `.Web` 是启动项目,运行应用程序后会在你的浏览器打开一个 **welcome** 页面.
|
||||
|
||||

|
||||
|
||||
点击 **login** 按钮重定向到 `Identity Server` 来登录应用程序.
|
||||
|
||||

|
||||
|
||||
{{ else }}
|
||||
|
||||
最后确保 `.Web` 是启动项目,运行应用程序后会在你的浏览器打开一个 **login** 页面.
|
||||
|
||||
> 在Visual Studio中使用Ctrl+F5(而不是F5)运行应用,如果你不用于调试,这会减少启动时间.
|
||||
|
||||

|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ else if UI != "MVC" }}
|
||||
|
||||
#### 运行HTTP API Host (服务器端)
|
||||
|
||||
{{ if Tiered == "Yes" }}
|
||||
|
||||
确保 `.IdentityServer` 是启动项目,运行应用程序后会在你的浏览器打开一个 **login** 页面.
|
||||
|
||||
> 在Visual Studio中使用Ctrl+F5(而不是F5)运行应用,如果你不用于调试,这会减少启动时间.
|
||||
|
||||
你可以登录,但是不能在这里进入主应用程序,它仅是验证服务器.
|
||||
|
||||
{{ end }}
|
||||
|
||||
确保 `.HttpApi.Host` 是启动项目,运行应用程序后会在你的浏览器打开一个 **Swagger UI** 页面.
|
||||
|
||||
{{ if Tiered == "No" }}
|
||||
|
||||
> 在Visual Studio中使用Ctrl+F5(而不是F5)运行应用,如果你不用于调试,这会减少启动时间.
|
||||
|
||||
{{ end }}
|
||||
|
||||

|
||||
|
||||
你可以看到应用程序的API并进行测试. 更多信息,请参阅[Swagger UI](https://swagger.io/tools/swagger-ui/).
|
||||
|
||||
> ##### Swagger UI 授权
|
||||
>
|
||||
> 大多数的HTTP API都需要身份验证和授权. 如果你要测试授权API, 请手动进入 `/Account/Login` 页面, 输入用户名: `admin` 和密码: `1q2w3E*` 登录到应用程序. 然后你可以访问授权API.
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ if UI == "NG" }}
|
||||
#### 运行 Angular 应用程序 (客户端)
|
||||
|
||||
在 `angular` 下打开命令行终端, 输入 `yarn` 命令(我们推荐使用[yarn](https://yarnpkg.com/)包管理, `npm install` 在大多数情况下也可以工作).
|
||||
|
||||
```bash
|
||||
yarn
|
||||
```
|
||||
|
||||
等到所有node模块加载成功, 执行 `yarn start` (或 `npm start`) 命令:
|
||||
|
||||
```bash
|
||||
yarn start
|
||||
```
|
||||
|
||||
等待 `Angular CLI` 使用 `BrowserSync` 启动 `Webpack` dev-server.
|
||||
它会负责编译你的 `TypeScript`代码, 并自动重新加载浏览器.
|
||||
完成后 `Angular Live Development Server` 会监听 localhost:4200.
|
||||
打开你的浏览器并导航到[localhost:4200](http://localhost:4200/).
|
||||
|
||||

|
||||
|
||||
{{ end }}
|
||||
|
||||
输入用户名 **admin**,密码 **1q2w3E*** 登录到应用程序.
|
||||
|
||||

|
||||
|
||||
应用程序已经启动并执行,你可以基于该启动模板开发应用程序.
|
||||
|
||||
#### 移动开发
|
||||
|
||||
ABP平台提供了[React Native](https://reactnative.dev/)模板用于开发移动应用程序.
|
||||
|
||||
> 该解决方案默认 `react-native` 包含了React Native应用程序,如果你不计划使用React Native开发移动应用程序,你可以忽略并删除 `react-native` 文件夹.
|
||||
|
||||
运行在Android模拟器或真机上的React Native应用程序无法连接到 `localhost` 上的后.要修复此问题,需要在本地IP上运行后端.
|
||||
|
||||
{{ if Tiered == "No"}}
|
||||

|
||||
|
||||
* 打开 `.HttpApi.Host` 文件夹下的 `appsettings.json` 文件. 将 `SelfUrl` 和 `Authority` 属性的 `localhost` 替换为你本地的IP地址.
|
||||
* 打开 `.HttpApi.Host/Properties` 文件夹下的 `launchSettings.json` 文件. 将 `applicationUrl` 属性的 `localhost` 替换为你本地的IP地址.
|
||||
|
||||
{{ else if Tiered == "Yes" }}
|
||||
|
||||

|
||||
|
||||
* 打开 `.IdentityServer` 文件夹下的 `appsettings.json` 文件. 将 `SelfUrl` 属性的 `localhost` 替换为你本地的IP地址.
|
||||
* 打开 `.IdentityServer/Properties` 文件夹下的 `launchSettings.json` 文件. 将 `applicationUrl` 属性的 `localhost` 替换为你本地的IP地址.
|
||||
* 打开 `.HttpApi.Host` 文件夹下的 `appsettings.json` 文件. 将 `Authority` 属性的 `localhost` 替换为你本地的IP地址.
|
||||
* 打开 `.HttpApi.Host/Properties` 文件夹下的 `launchSettings.json` 文件. 将 `applicationUrl` 属性的 `localhost` 替换为你本地的IP地址.
|
||||
|
||||
{{ end }}
|
||||
|
||||
按照**运行HTTP API Host (服务端口)**那样运行后端.
|
||||
|
||||
> React Native应用程序不信任自动生成的.NET HTTPS证书,你可以在开发期间使用HTTP.
|
||||
|
||||
在 `react-native` 文件夹打开命令行终端,输入 `yarn` 命令(我们推荐使用[yarn](https://yarnpkg.com/)包管理, `npm install` 在大多数情况下也可以工作).
|
||||
|
||||
```bash
|
||||
yarn
|
||||
```
|
||||
|
||||
* 打开 `react-nativer` 文件夹下的 `Environment.js` 文件. 将 `apiUrl` 和 `issuer` 属性的 `localhost` 替换为你本地的IP地址:
|
||||
|
||||

|
||||
|
||||
{{ if Tiered == "Yes" }}
|
||||
|
||||
> 确保 `issuer` 与正在运行的 `.IdentityServer` 项目匹配, `apiUrl` 与正在运行的 `.HttpApi.Host` 项目匹配.
|
||||
|
||||
{{else}}
|
||||
|
||||
> 确保 `issuer` 和 `apiUrl` 与正在运行的 `.HttpApi.Host` 项目匹配
|
||||
|
||||
{{ end }}
|
||||
|
||||
等到所有node模块加载成功, 执行 `yarn start` (或 `npm start`) 命令:
|
||||
|
||||
```bash
|
||||
yarn start
|
||||
```
|
||||
|
||||
等待Expo CLI启动后Expo CLI在 `http://localhost:19002/` 地址要开管理页面.
|
||||
|
||||

|
||||
|
||||
在上面的管理界面中,可以通过使用[Expo Client](https://expo.io/tools#client)扫描二维码,使用Android模拟器,iOS模拟器或真机来启动应用程序.
|
||||
|
||||
> 请参阅[expo.io](https://docs.expo.io/versions/v36.0.0/workflow/ios-simulator/)上的[Android Studio模拟器](https://docs.expo.io/versions/v36.0.0/workflow/android-studio-emulator/)和[iOS模拟器文档](https://docs.expo.io/versions/v36.0.0/workflow/android-studio-emulator/).
|
||||
|
||||

|
||||
|
||||
输入用户名 **admin**,密码 **1q2w3E*** 登录到应用程序.
|
||||
|
||||
应用程序已经启动并执行,你可以基于该启动模板开发应用程序.
|
||||
|
||||
> [应用程序启动模板](Startup-Templates/Application.md) 包含租户管理和Identity模块.
|
||||
|
||||
## 下一步是什么?
|
||||
|
||||
[应用程序开发教程](Tutorials/Part-1.md)
|
||||
@ -0,0 +1,3 @@
|
||||
## 规约
|
||||
|
||||
TODO..
|
||||
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 34 KiB |
@ -0,0 +1,198 @@
|
||||
## 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).
|
||||
|
||||
### 解决方案中的测试项目
|
||||
|
||||
解决方案中有多个测试项目:
|
||||
|
||||

|
||||
|
||||
每个项目用于测试相关的应用程序项目.测试项目使用以下库进行测试:
|
||||
|
||||
* [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 -> 测试资源管理器)并**执行**所有测试:
|
||||
|
||||

|
||||
|
||||
恭喜, 绿色图标表示测试已成功通过!
|
||||
|
After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 177 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 195 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 810 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 42 KiB |
@ -0,0 +1,181 @@
|
||||
# 确认弹层
|
||||
|
||||
你可以使用@abp/ng.theme.shared包中提供 `ConfirmationService` 放置在你项目的级别来显示确认弹层
|
||||
|
||||
## 入门
|
||||
|
||||
你不必在模块或组件级别提供 `ConfirmationService`,它已经在**根**级别提供,你可以在你的组件,指令或服务直接注入并使用它.
|
||||
|
||||
```js
|
||||
import { ConfirmationService } from '@abp/ng.theme.shared';
|
||||
|
||||
@Component({
|
||||
/* class metadata here */
|
||||
})
|
||||
class DemoComponent {
|
||||
constructor(private confirmation: ConfirmationService) {}
|
||||
}
|
||||
```
|
||||
|
||||
## 用法
|
||||
|
||||
你可以使用 `ConfirmationService` 的 `success`, `warn`, `error` 和 `info` 方法显示一个确认弹层.
|
||||
|
||||
### 如何显示一个确认弹层
|
||||
|
||||
```js
|
||||
const confirmationStatus$ = this.confirmation.success('Message', 'Title');
|
||||
```
|
||||
|
||||
- `ConfirmationService` 方法接收三个参数,分别是 `message`, `title`, 和 `options`.
|
||||
- `success`, `warn`, `error`, 和 `info` 方法返回一个[RxJS Subject](https://rxjs-dev.firebaseapp.com/guide/subject)监听确认弹层关闭事件. 事件值类型是 [`Confirmation.Status`](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/theme-shared/src/lib/models/confirmation.ts#L24)枚举.
|
||||
|
||||
### 如何监听关闭事件
|
||||
|
||||
你可以订阅确认弹层关闭事件,例:
|
||||
|
||||
```js
|
||||
import { Confirmation, ConfirmationService } from '@abp/ng.theme.shared';
|
||||
|
||||
constructor(private confirmation: ConfirmationService) {}
|
||||
|
||||
this.confirmation
|
||||
.warn('::WillBeDeleted', { key: '::AreYouSure', defaultValue: 'Are you sure?' })
|
||||
.subscribe((status: Confirmation.Status) => {
|
||||
// your code here
|
||||
});
|
||||
```
|
||||
|
||||
- `message` 和 `title` 参数接收字符串,本地化Key或本地化对象. 参阅[本地化文档](./Localization.md)
|
||||
- `Confirmation.Status` 是一个枚举,具有三个属性;
|
||||
- `Confirmation.Status.confirm` 是一个关闭事件值,当通过确认按钮关闭弹出窗口时触发此事件.
|
||||
- `Confirmation.Status.reject` 是一个关闭事件值,当通过“取消”按钮关闭弹出窗口时触发此事件.
|
||||
- `Confirmation.Status.dismiss` 是一个关闭事件值,当通过按Escape键关闭弹出窗口时触发此事件.
|
||||
|
||||
如果你对确认状态不感兴趣,则不必订阅返回的observable:
|
||||
|
||||
```js
|
||||
this.confirmation.error('You are not authorized.', 'Error');
|
||||
```
|
||||
|
||||
### 如何显示具有给定选项的确认弹层
|
||||
|
||||
选项可以作为第三个参数传递给`success`, `warn`, `error`, 和 `info` 方法:
|
||||
|
||||
```js
|
||||
const options: Partial<Confirmation.Options> = {
|
||||
hideCancelBtn: false,
|
||||
hideYesBtn: false,
|
||||
cancelText: 'Close',
|
||||
yesText: 'Confirm',
|
||||
messageLocalizationParams: ['Demo'],
|
||||
titleLocalizationParams: [],
|
||||
};
|
||||
|
||||
this.confirmation.warn(
|
||||
'AbpIdentity::RoleDeletionConfirmationMessage',
|
||||
'Are you sure?',
|
||||
options,
|
||||
);
|
||||
```
|
||||
|
||||
- `hideCancelBtn` 选项为 `true` 时隐藏取消按钮. 默认值为 `false`.
|
||||
- `hideYesBtn` 选项为 `true` 时隐藏确认按钮. 默认值为 `false`.
|
||||
- `cancelText` 是取消按钮的文本,可以传递本地化键或本地化对象. 默认值是 `AbpUi::Cancel`.
|
||||
- `yesText` 是确定按钮的文本,可以传递本地化键或本地化对象. 默认值是 `AbpUi::Yes`.
|
||||
- `messageLocalizationParams`是用于消息本地化的插值参数.
|
||||
- `titleLocalizationParams` 是标题本地化的插值参数.
|
||||
|
||||
使用以上选项确认弹层窗口如下所示:
|
||||
|
||||

|
||||
|
||||
你可以传递HTML字符串作为标题,消息或按钮文本. 例如:
|
||||
|
||||
```js
|
||||
const options: Partial<Confirmation.Options> = {
|
||||
yesText: '<i class="fa fa-trash mr-1"></i>Yes, delete it',
|
||||
};
|
||||
|
||||
this.confirmation.warn(
|
||||
`
|
||||
<strong>Role Demo</strong> will be <strong>deleted</strong>
|
||||
<br>
|
||||
Do you confirm that?
|
||||
`,
|
||||
'<span class="my-custom-title">Are you sure?</span>',
|
||||
options,
|
||||
);
|
||||
```
|
||||
|
||||
由于这些值现在是HTML,因此应该手动处理本地化. 参阅[LocalizationService](./Localization#using-the-localization-service)了解如何实现.
|
||||
|
||||
> 注意,Angular会清除所有字符串,并且并非每个HTML字符串都可以使用. 仅显示被Angular视为"安全"的值.
|
||||
|
||||
### 如何删除一个确认弹层
|
||||
|
||||
打开的确认弹出窗口可以通过 `clear` 方法手动删除:
|
||||
|
||||
```js
|
||||
this.confirmation.clear();
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### success
|
||||
|
||||
```js
|
||||
success(
|
||||
message: Config.LocalizationParam,
|
||||
title: Config.LocalizationParam,
|
||||
options?: Partial<Confirmation.Options>,
|
||||
): Observable<Confirmation.Status>
|
||||
```
|
||||
|
||||
> 请参见[`Config.LocalizationParam`类型](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/core/src/lib/models/config.ts#L46)和[Confirmation名称空间](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/theme-shared/src/lib/models/confirmation.ts)
|
||||
|
||||
|
||||
### warn
|
||||
|
||||
```js
|
||||
warn(
|
||||
message: Config.LocalizationParam,
|
||||
title: Config.LocalizationParam,
|
||||
options?: Partial<Confirmation.Options>,
|
||||
): Observable<Confirmation.Status>
|
||||
```
|
||||
|
||||
### error
|
||||
|
||||
```js
|
||||
error(
|
||||
message: Config.LocalizationParam,
|
||||
title: Config.LocalizationParam,
|
||||
options?: Partial<Confirmation.Options>,
|
||||
): Observable<Confirmation.Status>
|
||||
```
|
||||
|
||||
### info
|
||||
|
||||
```js
|
||||
info(
|
||||
message: Config.LocalizationParam,
|
||||
title: Config.LocalizationParam,
|
||||
options?: Partial<Confirmation.Options>,
|
||||
): Observable<Confirmation.Status>
|
||||
```
|
||||
|
||||
### clear
|
||||
|
||||
```js
|
||||
clear(
|
||||
status: Confirmation.Status = Confirmation.Status.dismiss
|
||||
): void
|
||||
```
|
||||
|
||||
- `status` 参数是确认关闭事件的值.
|
||||
|
||||
|
||||
## 下一步是什么?
|
||||
|
||||
- [Toast Overlay](./Toaster-Service.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属性后,导航菜单如下图所示:
|
||||
|
||||

|
||||
|
||||
## 通过 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'`.
|
||||
|
||||
新路由看起来像这样:
|
||||
|
||||

|
||||
|
||||
## 如何修改一个导航元素
|
||||
|
||||
`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_ 的子路由.
|
||||
|
||||
修改后,导航元素看起来像这样:
|
||||
|
||||

|
||||
|
||||
## 如何在菜单的右侧添加元素
|
||||
|
||||
右侧的元素存储在 @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
|
||||
|
||||

|
||||
|
||||
## 如何删除右侧菜单元素
|
||||
|
||||
TODO
|
||||
|
||||
## 下一步是什么?
|
||||
|
||||
* [组件替换](./Component-Replacement.md)
|
||||
@ -0,0 +1,179 @@
|
||||
# ProjectionStrategy
|
||||
|
||||
`ProjectionStrategy` 是@abp/ng.core包暴露出的抽象类. 有三种扩展它的投影策略: `ComponentProjectionStrategy`, `RootComponentProjectionStrategy` 和 `TemplateProjectionStrategy`. 它们实现相同的方法和属性,均可以帮助你定义内容投影的工作方式.
|
||||
|
||||
## ComponentProjectionStrategy
|
||||
|
||||
`ComponentProjectionStrategy` 是扩展 `ProjectionStrategy` 的类. 它使你可以将**组件投影到容器中**.
|
||||
|
||||
### constructor
|
||||
|
||||
```js
|
||||
constructor(
|
||||
component: T,
|
||||
private containerStrategy: ContainerStrategy,
|
||||
private contextStrategy?: ContextStrategy,
|
||||
)
|
||||
```
|
||||
|
||||
- `component` 是你要投影的组件的类.
|
||||
- `containerStrategy` 是在投影组件时将使用的 `ContainerStrategy`.
|
||||
- `contextStrategy` 是将在投影组件上使用的 `ContextStrategy`. (默认值: None_)
|
||||
|
||||
请参阅[ContainerStrategy](./Container-Strategy.md)和[ContextStrategy](./Context-Strategy.md)文档以了解其用法.
|
||||
|
||||
### injectContent
|
||||
|
||||
```js
|
||||
injectContent(injector: Injector): ComponentRef<T>
|
||||
```
|
||||
|
||||
该方法准备容器,解析组件,设置其上下文并将其投影到容器中. 它返回一个 `ComponentRef` 实例,你应该保留该实例以便以后清除投影的组件.
|
||||
|
||||
## RootComponentProjectionStrategy
|
||||
|
||||
`RootComponentProjectionStrategy` 是扩展 `ProjectionStrategy` 的类. 它使你可以将**组件投影到文档中**,例如将其附加到 `<body>`.
|
||||
|
||||
### constructor
|
||||
|
||||
```js
|
||||
constructor(
|
||||
component: T,
|
||||
private contextStrategy?: ContextStrategy,
|
||||
private domStrategy?: DomStrategy,
|
||||
)
|
||||
```
|
||||
|
||||
- `component` 是你要投影的组件的类.
|
||||
- `contextStrategy` 是将在投影组件上使用的 `ContextStrategy`. (默认值: None_)
|
||||
- `domStrategy` 是插入组件时将使用的 `DomStrategy`. (默认值: AppendToBody_)
|
||||
|
||||
请参阅[ContextStrategy](./Context-Strategy.md)和[DomStrategy](./Dom-Strategy.md)文档以了解其用法.
|
||||
|
||||
### injectContent
|
||||
|
||||
```js
|
||||
injectContent(injector: Injector): ComponentRef<T>
|
||||
```
|
||||
|
||||
该方法解析组件,设置其上下文并将其投影到文档中. 它返回一个 `ComponentRef` 实例,你应该保留该实例以便以后清除投影的组件.
|
||||
|
||||
## TemplateProjectionStrategy
|
||||
|
||||
`TemplateProjectionStrategy` 是扩展 `ProjectionStrategy` 的类.它使你可以将**模板投影到容器中**.
|
||||
|
||||
### constructor
|
||||
|
||||
```js
|
||||
constructor(
|
||||
template: T,
|
||||
private containerStrategy: ContainerStrategy,
|
||||
private contextStrategy?: ContextStrategy,
|
||||
)
|
||||
```
|
||||
|
||||
- `template` 是你要投影的 `TemplateRef`.
|
||||
- `containerStrategy` 是在投影组件时将使用的 `ContainerStrategy`.
|
||||
- `contextStrategy` 是将在投影组件上使用的 `ContextStrategy`. (默认值: None_)
|
||||
|
||||
请参阅[ContainerStrategy](./Container-Strategy.md)和[ContextStrategy](./Context-Strategy.md)文档以了解其用法.
|
||||
|
||||
### injectContent
|
||||
|
||||
```js
|
||||
injectContent(): EmbeddedViewRef<T>
|
||||
```
|
||||
|
||||
该方法准备容器,并将模板及其定义的上下文一起投影到容器. 它返回一个 `EmbeddedViewRef` 实例,你应该保留该实例以便以后清除投影的模板.
|
||||
|
||||
## 预定义的投影策略
|
||||
|
||||
可以通过 `PROJECTION_STRATEGY` 常量访问预定义的投影策略.
|
||||
|
||||
### AppendComponentToBody
|
||||
|
||||
```js
|
||||
PROJECTION_STRATEGY.AppendComponentToBody(
|
||||
component: T,
|
||||
contextStrategy?: ComponentContextStrategy<T>,
|
||||
)
|
||||
```
|
||||
|
||||
将给定上下文设置到组件并将放置在文档中 `<body>` 标签的**末尾**.
|
||||
|
||||
### AppendComponentToContainer
|
||||
|
||||
```js
|
||||
PROJECTION_STRATEGY.AppendComponentToContainer(
|
||||
component: T,
|
||||
containerRef: ViewContainerRef,
|
||||
contextStrategy?: ComponentContextStrategy<T>,
|
||||
)
|
||||
```
|
||||
|
||||
将给定上下文设置到组件并将放置在容器的**末尾**.
|
||||
|
||||
### AppendTemplateToContainer
|
||||
|
||||
```js
|
||||
PROJECTION_STRATEGY.AppendTemplateToContainer(
|
||||
templateRef: T,
|
||||
containerRef: ViewContainerRef,
|
||||
contextStrategy?: ComponentContextStrategy<T>,
|
||||
)
|
||||
```
|
||||
|
||||
将给定上下文设置到模板并将其放置在容器的**末尾**.
|
||||
|
||||
### PrependComponentToContainer
|
||||
|
||||
```js
|
||||
PROJECTION_STRATEGY.PrependComponentToContainer(
|
||||
component: T,
|
||||
containerRef: ViewContainerRef,
|
||||
contextStrategy?: ComponentContextStrategy<T>,
|
||||
)
|
||||
```
|
||||
|
||||
将给定上下文设置到组件并将其放置在容器的**开头**.
|
||||
|
||||
### PrependTemplateToContainer
|
||||
|
||||
```js
|
||||
PROJECTION_STRATEGY.PrependTemplateToContainer(
|
||||
templateRef: T,
|
||||
containerRef: ViewContainerRef,
|
||||
contextStrategy?: ComponentContextStrategy<T>,
|
||||
)
|
||||
```
|
||||
|
||||
将给定上下文设置到模板并将其放置在容器的**开头**.
|
||||
|
||||
|
||||
### ProjectComponentToContainer
|
||||
|
||||
```js
|
||||
PROJECTION_STRATEGY.ProjectComponentToContainer(
|
||||
component: T,
|
||||
containerRef: ViewContainerRef,
|
||||
contextStrategy?: ComponentContextStrategy<T>,
|
||||
)
|
||||
```
|
||||
|
||||
清除容器,将给定的上下文设置到组件并放在**已清除**的容器中.
|
||||
|
||||
### ProjectTemplateToContainer
|
||||
|
||||
```js
|
||||
PROJECTION_STRATEGY.ProjectTemplateToContainer(
|
||||
templateRef: T,
|
||||
containerRef: ViewContainerRef,
|
||||
contextStrategy?: ComponentContextStrategy<T>,
|
||||
)
|
||||
```
|
||||
|
||||
清除容器,将给定的上下文设置到模板并放在**已清除**的容器中.
|
||||
|
||||
## 另请参阅
|
||||
|
||||
- [DomInsertionService](./Dom-Insertion-Service.md)
|
||||