Browse Source

Merge branch 'dev' into extra-properties

pull/3834/head
Halil İbrahim Kalkan 7 years ago
parent
commit
431ab55dea
  1. 31
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  2. 6
      common.DotSettings
  3. 8
      docs/en/CLI.md
  4. 4
      docs/en/Getting-Started.md
  5. 8
      docs/en/Tutorials/Part-1.md
  6. 54
      docs/en/Tutorials/Part-2.md
  7. BIN
      docs/en/Tutorials/images/bookstore-angular-file-tree.png
  8. 12
      docs/en/UI/Angular/Config-State.md
  9. 22
      docs/en/UI/Angular/Confirmation-Service.md
  10. 199
      docs/en/UI/Angular/Modifying-the-Menu.md
  11. 1
      docs/en/UI/Angular/Toaster-Service.md
  12. BIN
      docs/en/UI/Angular/images/navigation-menu-after-patching.png
  13. BIN
      docs/en/UI/Angular/images/navigation-menu-search-input.png
  14. BIN
      docs/en/UI/Angular/images/navigation-menu-via-app-routing.png
  15. BIN
      docs/en/UI/Angular/images/navigation-menu-via-config-state.png
  16. 22
      docs/en/UI/Common/Utils/Linked-List.md
  17. 6
      docs/en/docs-nav.json
  18. 6
      docs/zh-Hans/CLI.md
  19. 9
      docs/zh-Hans/Getting-Started-Angular-Template.md
  20. 2
      docs/zh-Hans/Getting-Started-AspNetCore-Application.md
  21. 104
      docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md
  22. 8
      docs/zh-Hans/Getting-Started-With-Startup-Templates.md
  23. 411
      docs/zh-Hans/Getting-Started.md
  24. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-book-list-2.png
  25. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-book-list.png
  26. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-template.png
  27. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files-v2.png
  28. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-new-book-button.png
  29. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration-v2.png
  30. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-swagger.png
  31. 1078
      docs/zh-Hans/Tutorials/Part-1.md
  32. 1
      docs/zh-Hans/Tutorials/Part-2.md
  33. 1
      docs/zh-Hans/Tutorials/Part-3.md
  34. BIN
      docs/zh-Hans/Tutorials/images/bookstore-actions-buttons.png
  35. 0
      docs/zh-Hans/Tutorials/images/bookstore-add-create-dialog-v2.png
  36. 0
      docs/zh-Hans/Tutorials/images/bookstore-add-edit-dialog.png
  37. 0
      docs/zh-Hans/Tutorials/images/bookstore-add-index-page-v2.png
  38. BIN
      docs/zh-Hans/Tutorials/images/bookstore-angular-file-tree.png
  39. 0
      docs/zh-Hans/Tutorials/images/bookstore-appservice-tests.png
  40. BIN
      docs/zh-Hans/Tutorials/images/bookstore-book-list-2.png
  41. BIN
      docs/zh-Hans/Tutorials/images/bookstore-book-list.png
  42. 0
      docs/zh-Hans/Tutorials/images/bookstore-books-table-actions.png
  43. 0
      docs/zh-Hans/Tutorials/images/bookstore-books-table.png
  44. BIN
      docs/zh-Hans/Tutorials/images/bookstore-confirmation-popup.png
  45. 0
      docs/zh-Hans/Tutorials/images/bookstore-create-dialog-2.png
  46. 0
      docs/zh-Hans/Tutorials/images/bookstore-create-dialog.png
  47. BIN
      docs/zh-Hans/Tutorials/images/bookstore-create-project-angular.png
  48. BIN
      docs/zh-Hans/Tutorials/images/bookstore-create-project-mvc.png
  49. BIN
      docs/zh-Hans/Tutorials/images/bookstore-creating-book-list-terminal.png
  50. BIN
      docs/zh-Hans/Tutorials/images/bookstore-creating-books-module-terminal.png
  51. BIN
      docs/zh-Hans/Tutorials/images/bookstore-database-tables-ef.png
  52. BIN
      docs/zh-Hans/Tutorials/images/bookstore-database-tables-mongodb.png
  53. BIN
      docs/zh-Hans/Tutorials/images/bookstore-edit-button.png
  54. BIN
      docs/zh-Hans/Tutorials/images/bookstore-empty-new-book-modal.png
  55. BIN
      docs/zh-Hans/Tutorials/images/bookstore-final-actions-dropdown.png
  56. BIN
      docs/zh-Hans/Tutorials/images/bookstore-generate-state-books.png
  57. 0
      docs/zh-Hans/Tutorials/images/bookstore-homepage.png
  58. 0
      docs/zh-Hans/Tutorials/images/bookstore-index-js-file-v2.png
  59. BIN
      docs/zh-Hans/Tutorials/images/bookstore-initial-book-list-page.png
  60. BIN
      docs/zh-Hans/Tutorials/images/bookstore-initial-books-page-with-layout.png
  61. BIN
      docs/zh-Hans/Tutorials/images/bookstore-localization-files-v2.png
  62. 0
      docs/zh-Hans/Tutorials/images/bookstore-menu-items.png
  63. BIN
      docs/zh-Hans/Tutorials/images/bookstore-migrations-applied-angular.png
  64. BIN
      docs/zh-Hans/Tutorials/images/bookstore-migrations-applied-mvc.png
  65. BIN
      docs/zh-Hans/Tutorials/images/bookstore-new-book-button.png
  66. BIN
      docs/zh-Hans/Tutorials/images/bookstore-new-book-form-v2.png
  67. BIN
      docs/zh-Hans/Tutorials/images/bookstore-new-book-form.png
  68. BIN
      docs/zh-Hans/Tutorials/images/bookstore-new-menu-item.png
  69. BIN
      docs/zh-Hans/Tutorials/images/bookstore-open-package-manager-console.png
  70. BIN
      docs/zh-Hans/Tutorials/images/bookstore-pmc-add-book-migration-v2.png
  71. 0
      docs/zh-Hans/Tutorials/images/bookstore-pmc-add-book-migration.png
  72. BIN
      docs/zh-Hans/Tutorials/images/bookstore-service-terminal-output.png
  73. BIN
      docs/zh-Hans/Tutorials/images/bookstore-solution-structure-angular.png
  74. 0
      docs/zh-Hans/Tutorials/images/bookstore-solution-structure-mvc.png
  75. BIN
      docs/zh-Hans/Tutorials/images/bookstore-start-project-angular.png
  76. BIN
      docs/zh-Hans/Tutorials/images/bookstore-start-project-mvc.png
  77. BIN
      docs/zh-Hans/Tutorials/images/bookstore-swagger-book-dto-properties.png
  78. BIN
      docs/zh-Hans/Tutorials/images/bookstore-swagger.png
  79. 0
      docs/zh-Hans/Tutorials/images/bookstore-test-js-proxy-getlist-network.png
  80. 0
      docs/zh-Hans/Tutorials/images/bookstore-test-js-proxy-getlist.png
  81. BIN
      docs/zh-Hans/Tutorials/images/bookstore-test-projects-angular.png
  82. 0
      docs/zh-Hans/Tutorials/images/bookstore-test-projects-mvc.png
  83. BIN
      docs/zh-Hans/Tutorials/images/bookstore-test-projects-v2.png
  84. BIN
      docs/zh-Hans/Tutorials/images/bookstore-update-database-after-book-entity.png
  85. 0
      docs/zh-Hans/Tutorials/images/bookstore-user-management.png
  86. BIN
      docs/zh-Hans/Tutorials/images/bookstore-visual-studio-solution-v3.png
  87. BIN
      docs/zh-Hans/Tutorials/images/generate-proxy-command.png
  88. BIN
      docs/zh-Hans/Tutorials/images/generated-proxies.png
  89. BIN
      docs/zh-Hans/Tutorials/images/mozilla-self-signed-cert-error.png
  90. 181
      docs/zh-Hans/UI/Angular/Confirmation-Service.md
  91. 2
      docs/zh-Hans/UI/Angular/Permission-Management.md
  92. 155
      docs/zh-Hans/UI/Angular/Toaster-Service.md
  93. BIN
      docs/zh-Hans/UI/Angular/images/confirmation.png
  94. BIN
      docs/zh-Hans/UI/Angular/images/toast.png
  95. 22
      docs/zh-Hans/UI/Common/Utils/Linked-List.md
  96. 31
      docs/zh-Hans/docs-nav.json
  97. 28
      docs/zh-Hans/docs-params.json
  98. 16
      framework/Volo.Abp.sln
  99. 12
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs
  100. 11
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs

31
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json

@ -13,8 +13,10 @@
"Permission:Edit": "Edit", "Permission:Edit": "Edit",
"Permission:Delete": "Delete", "Permission:Delete": "Delete",
"Permission:Create": "Create", "Permission:Create": "Create",
"Permission:Accounting": "Accounting",
"Permission:Accounting:Quotation": "Quotation",
"Menu:Organizations": "Organizations", "Menu:Organizations": "Organizations",
"Menu:Accounting": "Accounting", "Menu:Accounting": "Accounting",
"Menu:Packages": "Packages", "Menu:Packages": "Packages",
"NpmPackageDeletionWarningMessage": "This NPM Package will be deleted. Do you confirm that?", "NpmPackageDeletionWarningMessage": "This NPM Package will be deleted. Do you confirm that?",
"NugetPackageDeletionWarningMessage": "This Nuget Package will be deleted. Do you confirm that?", "NugetPackageDeletionWarningMessage": "This Nuget Package will be deleted. Do you confirm that?",
@ -94,13 +96,30 @@
"UsernameOrEmail": "Username or email", "UsernameOrEmail": "Username or email",
"UsernameOrEmailPlaceholder": "Username or email...", "UsernameOrEmailPlaceholder": "Username or email...",
"Member": "Member", "Member": "Member",
"QuotationPurchasedOrderNo": "Quotation Purchased Order No", "PurchaseOrderNo": "Purchase order no",
"QuotationTime": "Quotation Time", "QuotationDate": "Quotation date",
"CompanyName": "Company Name", "CompanyName": "Company name",
"CompanyAddress": "Company Address", "CompanyAddress": "Company address",
"Price": "Price", "Price": "Price",
<<<<<<< Updated upstream
"DiscountText": "Discount text",
"DiscountQuantity": "Discount quantity",
"DiscountPrice": "Discount price",
"Quotation": "Quotation",
"Generate": "Generate"
=======
"ExtraText": "Extra Text", "ExtraText": "Extra Text",
"ExtraAmount": "Extra Amount", "ExtraAmount": "Extra Amount",
"DownloadQuotation": "Download Quotation" "DownloadQuotation": "Download Quotation",
"Invoice": "Invoice",
"TaxNumber": "Tax Number",
"InvoiceNumber": "Invoice Number",
"InvoiceDate": "Invoice Date",
"Quantity": "Quantity",
"AddProduct": "Add Product",
"NeedToAddProduct": "Need to add product to generate invoice !",
"TotalPrice": "Total Price",
"Generate": "Generate"
>>>>>>> Stashed changes
} }
} }

6
common.DotSettings

@ -20,5 +20,11 @@
<s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Async/@EntryIndexedValue">False</s:String> <s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Async/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Mutable/@EntryIndexedValue">False</s:String> <s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Mutable/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SQL/@EntryIndexedValue">SQL</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SQL/@EntryIndexedValue">SQL</s:String>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/HideTypeNameHintsWhenTypeNameIsEvidentFromVariableName/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowMethodReturnTypeNameHints/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForImplicitlyTypedVariables/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForLambdaExpressionParameters/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForLinqQueryRangeVariables/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForPatternMatchingExpressions/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Volo/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Volo/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary> </wpf:ResourceDictionary>

8
docs/en/CLI.md

@ -135,6 +135,8 @@ abp update [options]
* `--include-previews` or `-p`: Includes preview, beta and rc packages while checking the latest versions. * `--include-previews` or `-p`: Includes preview, beta and rc packages while checking the latest versions.
* `--npm`: Only updates NPM packages. * `--npm`: Only updates NPM packages.
* `--nuget`: Only updates NuGet packages. * `--nuget`: Only updates NuGet packages.
* `--solution-path` or `-sp`: Specify the solution path. Use the current directory by default
* `--solution-name` or `-sn`: Specify the solution name. Search `*.sln` files in the directory by default.
### switch-to-preview ### switch-to-preview
@ -170,7 +172,11 @@ Some features of the CLI requires to be logged in to abp.io platform. To login w
abp login <username> abp login <username>
``` ```
Notice that, a new login with an already active session, will kill the previous session and creates a new one. ```bash
abp login <username> -p <password>
```
Notice that, a new login with an already active session, overwrites the previous session.
### logout ### logout

4
docs/en/Getting-Started.md

@ -400,8 +400,8 @@ Enter **admin** as the username and **1q2w3E*** as the password to login to the
The application is up and running. You can continue to develop your application based on this startup template. The application is up and running. You can continue to develop your application based on this startup template.
> The [application startup template](startup-templates/application/index.md) includes the TenantManagement and Identity modules. > The [application startup template](Startup-Templates/Application.md) includes the TenantManagement and Identity modules.
## What's next? ## What's next?
[Application development tutorial](tutorials/book-store/part-1.md) [Application development tutorial](Tutorials/Part-1.md)

8
docs/en/Tutorials/Part-1.md

@ -38,7 +38,7 @@ Create a new project named `Acme.BookStore` where `Acme` is the company name and
#### Create the project #### Create the project
By running the below command, it creates a new ABP project with the database provider `{{DB_Text}}` and UI option `MVC`. To see the other CLI options, check out [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) document. By running the below command, it creates a new ABP project with the database provider `{{DB_Text}}` and UI option `{{UI_Value}}`. To see the other CLI options, check out [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) document.
```bash ```bash
abp new Acme.BookStore --template app --database-provider {{DB}} --ui {{UI_Text}} --mobile none abp new Acme.BookStore --template app --database-provider {{DB}} --ui {{UI_Text}} --mobile none
@ -61,7 +61,7 @@ After creating the project, you need to apply the initial migrations and create
To run the project, right click to the {{if UI == "MVC"}} `Acme.BookStore.Web`{{end}} {{if UI == "NG"}} `Acme.BookStore.HttpApi.Host` {{end}} project and click **Set As StartUp Project**. And run the web project by pressing **CTRL+F5** (*without debugging and fast*) or press **F5** (*with debugging and slow*). {{if UI == "NG"}}You will see the Swagger UI for BookStore API.{{end}} To run the project, right click to the {{if UI == "MVC"}} `Acme.BookStore.Web`{{end}} {{if UI == "NG"}} `Acme.BookStore.HttpApi.Host` {{end}} project and click **Set As StartUp Project**. And run the web project by pressing **CTRL+F5** (*without debugging and fast*) or press **F5** (*with debugging and slow*). {{if UI == "NG"}}You will see the Swagger UI for BookStore API.{{end}}
Further information, see the [running the application section](../../Getting-Started-{{if UI == "NG"}}Angular{{else}}AspNetCore-MVC{{end}}-Template#running-the-application).Getting-Started-AspNetCore-MVC-Template#running-the-application Further information, see the [running the application section](../Getting-Started?UI={{UI}}#run-the-application).
![Set as startup project](./images/bookstore-start-project-{{UI_Text}}.png) ![Set as startup project](./images/bookstore-start-project-{{UI_Text}}.png)
@ -335,7 +335,7 @@ INSERT INTO AppBooks (Id,CreationTime,[Name],[Type],PublishDate,Price) VALUES
### Create the application service ### Create the application service
The next step is to create an [application service](../../Application-Services.md) to manage the books which will allow us the four basic functions: creating, reading, updating and deleting. Application layer is separated into two projects: The next step is to create an [application service](../Application-Services.md) to manage the books which will allow us the four basic functions: creating, reading, updating and deleting. Application layer is separated into two projects:
* `Acme.BookStore.Application.Contracts` mainly contains your `DTO`s and application service interfaces. * `Acme.BookStore.Application.Contracts` mainly contains your `DTO`s and application service interfaces.
* `Acme.BookStore.Application` contains the implementations of your application services. * `Acme.BookStore.Application` contains the implementations of your application services.
@ -874,8 +874,6 @@ We'll see **book-list works!** text on the books page:
Run the following command in the terminal to create a new state, named `BooksState`: Run the following command in the terminal to create a new state, named `BooksState`:
![Initial book list page](./images/bookstore-generate-state-books.png)
```bash ```bash
npx @ngxs/cli --name books --directory src/app/books npx @ngxs/cli --name books --directory src/app/books
``` ```

54
docs/en/Tutorials/Part-2.md

@ -525,7 +525,7 @@ export class BooksState {
* We imported `CreateUpdateBook` action and defined the `save` method that will listen to a `CreateUpdateBook` action to create a book. * We imported `CreateUpdateBook` action and defined the `save` method that will listen to a `CreateUpdateBook` action to create a book.
When the `SaveBook` action dispatched, the save method is being executed. It calls `create` method of the `BookService`. When the `SaveBook` action dispatched, the save method is being executed. It calls `createByInput` method of the `BookService`.
#### Add a modal to BookListComponent #### Add a modal to BookListComponent
@ -885,32 +885,6 @@ Now, you can open your browser to see the changes:
#### Saving the book #### Saving the book
Open `book-list.component.html` in `app\books\book-list` folder and add the following `abp-button` to save the new book.
```html
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
{%{{{ 'AbpAccount::Close' | abpLocalization }}}%}
</button>
<!--added save button-->
<button class="btn btn-primary" (click)="save()" [disabled]="form.invalid">
<i class="fa fa-check mr-1"></i>
{%{{{ 'AbpAccount::Save' | abpLocalization }}}%}
</button>
</ng-template>
```
Find the `<form [formGroup]="form">` tag and replace below content:
```html
<form [formGroup]="form" (ngSubmit)="save()"> <!-- added the ngSubmit -->
```
* We added the `(ngSubmit)="save()"` to `<form>` element to save a new book by pressing the enter.
* We added `abp-button` to the bottom area of the modal to save a new book.
Open `book-list.component.ts` file in `app\books\book-list` folder and replace the content as below: Open `book-list.component.ts` file in `app\books\book-list` folder and replace the content as below:
```js ```js
@ -993,6 +967,32 @@ export class BookListComponent implements OnInit {
* We imported `CreateUpdateBook`. * We imported `CreateUpdateBook`.
* We added `save` method * We added `save` method
Open `book-list.component.html` in `app\books\book-list` folder and add the following `abp-button` to save the new book.
```html
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
{%{{{ 'AbpAccount::Close' | abpLocalization }}}%}
</button>
<!--added save button-->
<button class="btn btn-primary" (click)="save()" [disabled]="form.invalid">
<i class="fa fa-check mr-1"></i>
{%{{{ 'AbpAccount::Save' | abpLocalization }}}%}
</button>
</ng-template>
```
Find the `<form [formGroup]="form">` tag and replace below content:
```html
<form [formGroup]="form" (ngSubmit)="save()"> <!-- added the ngSubmit -->
```
* We added the `(ngSubmit)="save()"` to `<form>` element to save a new book by pressing the enter.
* We added `abp-button` to the bottom area of the modal to save a new book.
The final modal UI looks like below: The final modal UI looks like below:
![Save button to the modal](./images/bookstore-new-book-form-v2.png) ![Save button to the modal](./images/bookstore-new-book-form-v2.png)

BIN
docs/en/Tutorials/images/bookstore-angular-file-tree.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

After

Width:  |  Height:  |  Size: 91 KiB

12
docs/en/UI/Angular/Config-State.md

@ -236,7 +236,7 @@ const newRoute: ABP.Route = {
path: "page", path: "page",
invisible: false, invisible: false,
order: 2, order: 2,
requiredPolicy: "MyProjectName::MyNewPage" requiredPolicy: "MyProjectName.MyNewPage"
}; };
this.config.dispatchAddRoute(newRoute); this.config.dispatchAddRoute(newRoute);
@ -248,23 +248,25 @@ The `newRoute` will be placed as at root level, i.e. without any parent routes a
If you want **to add a child route, you can do this:** If you want **to add a child route, you can do this:**
```js ```js
import { eIdentityRouteNames } from '@abp/ng.identity';
// this.config is instance of ConfigStateService // this.config is instance of ConfigStateService
const newRoute: ABP.Route = { const newRoute: ABP.Route = {
parentName: "AbpAccount::Login", parentName: eIdentityRouteNames.IdentityManagement,
name: "My New Page", name: "My New Page",
iconClass: "fa fa-dashboard", iconClass: "fa fa-dashboard",
path: "page", path: "page",
invisible: false, invisible: false,
order: 2, order: 2,
requiredPolicy: "MyProjectName::MyNewPage" requiredPolicy: "MyProjectName.MyNewPage"
}; };
this.config.dispatchAddRoute(newRoute); this.config.dispatchAddRoute(newRoute);
// returns a state stream which emits after dispatch action is complete // returns a state stream which emits after dispatch action is complete
``` ```
The `newRoute` will then be placed as a child of the parent route named `'AbpAccount::Login'` and its url will be set as `'/account/login/page'`. The `newRoute` will then be placed as a child of the parent route named `eIdentityRouteNames.IdentityManagement` and its url will be set as `'/identity/page'`.
#### Route Configuration Properties #### Route Configuration Properties
@ -291,4 +293,4 @@ Please refer to `Config.Environment` type for all the properties you can pass to
## What's Next? ## What's Next?
* [Component Replacement](./Component-Replacement.md) - [Modifying the Menu](./Modifying-the-Menu.md)

22
docs/en/UI/Angular/Confirmation-Service.md

@ -94,6 +94,28 @@ With the options above, the confirmation popup looks like this:
![confirmation](./images/confirmation.png) ![confirmation](./images/confirmation.png)
You are able to pass in an HTML string as title, message, or button texts. Here is an example:
```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,
);
```
Since the values are HTML now, localization should be handled manually. Check out the [LocalizationService](./Localization#using-the-localization-service) to see how you can accomplish that.
> Please note that all strings will be sanitized by Angular and not every HTML string will work. Only values that are considered as "safe" by Angular will be displayed.
### How to Remove a Confirmation Popup ### How to Remove a Confirmation Popup
The open confirmation popup can be removed manually via the `clear` method: The open confirmation popup can be removed manually via the `clear` method:

199
docs/en/UI/Angular/Modifying-the-Menu.md

@ -0,0 +1,199 @@
# Modifying the Menu
The menu is inside the `ApplicationLayoutComponent` in the @abp/ng.theme.basic package. There are several methods for modifying the menu elements. This document covers these methods. If you would like to replace the menu completely, please refer to [Component Replacement documentation](./Component-Replacement.md) and learn how to replace a layout.
<!-- TODO: Replace layout replacement document with component replacement. Layout replacement document will be created.-->
## How to Add a Logo
The `logoUrl` property in the environment variables is the url of the logo.
You can add your logo to `src/assets` folder and set the `logoUrl` as shown below:
```js
export const environment = {
// other configurations
application: {
name: 'MyProjectName',
logoUrl: 'assets/logo.png',
},
// other configurations
};
```
## How to Add a Navigation Element
### Via `routes` Property in `AppRoutingModule`
You can define your routes by adding `routes` as a child property to `data` property of a route configuration in the `app-routing.module`. The `@abp/ng.core` package organizes your routes and stores them in the `ConfigState`. `ApplicationLayoutComponent` gets routes from store and displays them on the menu.
You can add the `routes` property like below:
```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` is the label of the navigation element. A localization key or a localization object can be passed.
- `order` is the order of the navigation element.
- `iconClass` is the class of the `i` tag, which is placed to the left of the navigation label.
- `requiredPolicy` is the permission key to access the page. See the [Permission Management document](./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.
After adding the `routes` property as described above, the navigation menu looks like this:
![navigation-menu-via-app-routing](./images/navigation-menu-via-app-routing.png)
## Via ConfigState
The `dispatchAddRoute` method of `ConfigStateService` adds a new navigation element to the menu.
```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
```
The `newRoute` will be placed as at root level, i.e. without any parent routes, and its url will be stored as `'/path'`.
If you want **to add a child route, you can do this:**
```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
```
The `newRoute` will then be placed as a child of the parent route named `eIdentityRouteNames.IdentityManagement` and its url will be set as `'/identity/page'`.
The new route will be added like below:
![navigation-menu-via-config-state](./images/navigation-menu-via-config-state.png)
## How to Patch a Navigation Element
The `dispatchPatchRouteByName` method finds a route by its name and replaces its configuration in the store with the new configuration passed as the second parameter.
```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
```
* Moved the _Home_ navigation under the _Administration_ dropdown based on given `parentName`.
* Added an icon.
* Specified the order.
* Added a child route named _Dashboard_.
After the patch above, navigation elements looks like below:
![navigation-menu-after-patching](./images/navigation-menu-after-patching.png)
## How to Add an Element to Right Part of the Menu
The right part elements are stored in the `LayoutState` that is in the @abp/ng.theme.basic package.
The `dispatchAddNavigationElement` method of the `LayoutStateService` adds an element to the right part of the menu.
You can insert an element by adding your template to `app.component` and calling the `dispatchAddNavigationElement` method:
```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);
}
}
```
This inserts a search input to the menu. The final UI looks like below:
![navigation-menu-search-input](./images/navigation-menu-search-input.png)
## How to Remove an Element From Right Part of the Menu
TODO
## What's Next
* [Component Replacement](./Component-Replacement.md)

1
docs/en/UI/Angular/Toaster-Service.md

@ -150,6 +150,7 @@ clear(): void
Removes all open toasts. Removes all open toasts.
## See Also ## See Also
- [Confirmation Popup](./Confirmation-Service.md) - [Confirmation Popup](./Confirmation-Service.md)
## What's Next? ## What's Next?

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

22
docs/en/UI/Common/Utils/Linked-List.md

@ -1270,7 +1270,7 @@ find(predicate: ListIteratorFn<T>): ListNode<T> | undefined
Finds the first node from the list that matches the given predicate: Finds the first node from the list that matches the given predicate:
```js ```js
list.addTailMany(['a', 'b', 'b', 'c']); list.addManyTail(['a', 'b', 'b', 'c']);
// "a" <-> "b" <-> "b" <-> "c" // "a" <-> "b" <-> "b" <-> "c"
@ -1294,7 +1294,7 @@ findIndex(predicate: ListIteratorFn<T>): number
Finds the position of the first node from the list that matches the given predicate: Finds the position of the first node from the list that matches the given predicate:
```js ```js
list.addTailMany(['a', 'b', 'b', 'c']); list.addManyTail(['a', 'b', 'b', 'c']);
// "a" <-> "b" <-> "b" <-> "c" // "a" <-> "b" <-> "b" <-> "c"
@ -1322,7 +1322,7 @@ get(position: number): ListNode<T> | undefined
Finds and returns the node with specific position in the list: Finds and returns the node with specific position in the list:
```js ```js
list.addTailMany(['a', 'b', 'c']); list.addManyTail(['a', 'b', 'c']);
// "a" <-> "b" <-> "c" // "a" <-> "b" <-> "c"
@ -1346,7 +1346,7 @@ indexOf(value: T, compareFn?: ListComparisonFn<T>): number
Finds the position of the first node from the list that has the given value: Finds the position of the first node from the list that has the given value:
```js ```js
list.addTailMany(['a', 'b', 'b', 'c']); list.addManyTail(['a', 'b', 'b', 'c']);
// "a" <-> "b" <-> "b" <-> "c" // "a" <-> "b" <-> "b" <-> "c"
@ -1368,7 +1368,7 @@ i3 === -1
You may pass a custom compare function to detect the searched value: You may pass a custom compare function to detect the searched value:
```js ```js
list.addTailMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]); list.addManyTail([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]);
// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3}
@ -1408,7 +1408,7 @@ forEach(iteratorFn: ListIteratorFn<T>): void
Runs a function on all nodes in a linked list from head to tail: Runs a function on all nodes in a linked list from head to tail:
```js ```js
list.addTailMany(['a', 'b', 'c']); list.addManyTail(['a', 'b', 'c']);
// "a" <-> "b" <-> "c" // "a" <-> "b" <-> "c"
@ -1425,7 +1425,7 @@ list.forEach((node, index) => console.log(node.value + index));
A linked list is iterable. In other words, you may use methods like `for...of` on it. A linked list is iterable. In other words, you may use methods like `for...of` on it.
```js ```js
list.addTailMany(['a', 'b', 'c']); list.addManyTail(['a', 'b', 'c']);
// "a" <-> "b" <-> "c" // "a" <-> "b" <-> "c"
@ -1449,7 +1449,7 @@ toArray(): T[]
Converts a linked list to an array of values: Converts a linked list to an array of values:
```js ```js
list.addTailMany(['a', 'b', 'c']); list.addManyTail(['a', 'b', 'c']);
// "a" <-> "b" <-> "c" // "a" <-> "b" <-> "c"
@ -1471,7 +1471,7 @@ toNodeArray(): ListNode<T>[]
Converts a linked list to an array of nodes: Converts a linked list to an array of nodes:
```js ```js
list.addTailMany(['a', 'b', 'c']); list.addManyTail(['a', 'b', 'c']);
// "a" <-> "b" <-> "c" // "a" <-> "b" <-> "c"
@ -1495,7 +1495,7 @@ toString(mapperFn: ListMapperFn<T> = JSON.stringify): string
Converts a linked list to a string representation of nodes and their relations: Converts a linked list to a string representation of nodes and their relations:
```js ```js
list.addTailMany(['a', 2, 'c', { k: 4, v: 'd' }]); list.addManyTail(['a', 2, 'c', { k: 4, v: 'd' }]);
// "a" <-> 2 <-> "c" <-> {"k":4,"v":"d"} // "a" <-> 2 <-> "c" <-> {"k":4,"v":"d"}
@ -1562,7 +1562,7 @@ export class ListNode<T = any> {
- `previous` refers to the previous node in the list. - `previous` refers to the previous node in the list.
```js ```js
list.addTailMany([ 0, 1, 2 ]); list.addManyTail([ 0, 1, 2 ]);
console.log( console.log(
list.head.value, // 0 list.head.value, // 0

6
docs/en/docs-nav.json

@ -352,6 +352,10 @@
"text": "Config State", "text": "Config State",
"path": "UI/Angular/Config-State.md" "path": "UI/Angular/Config-State.md"
}, },
{
"text": "Modifying the Menu",
"path": "UI/Angular/Modifying-the-Menu.md"
},
{ {
"text": "Component Replacement", "text": "Component Replacement",
"path": "UI/Angular/Component-Replacement.md" "path": "UI/Angular/Component-Replacement.md"
@ -398,7 +402,7 @@
"text": "Data Access", "text": "Data Access",
"items": [ "items": [
{ {
"text": "Overall", "text": "Overall",
"path": "Data-Access.md" "path": "Data-Access.md"
}, },
{ {

6
docs/zh-Hans/CLI.md

@ -135,6 +135,8 @@ abp update [options]
* `--include-previews` 或 `-p`: 将预览版, 测试版本 和 rc 包 同时更新到最新版本. * `--include-previews` 或 `-p`: 将预览版, 测试版本 和 rc 包 同时更新到最新版本.
* `--npm`: 仅更新NPM包 * `--npm`: 仅更新NPM包
* `--nuget`: 仅更新的NuGet包 * `--nuget`: 仅更新的NuGet包
* `--solution-path` 或 `-sp`: 指定解决方案路径/目录. 默认使用当前目录
* `--solution-name` 或 `-sn`: 指定解决方案名称. 默认在目录中搜索`*.sln`文件.
### 切换到每晚构建(预览)包 ### 切换到每晚构建(预览)包
@ -164,6 +166,10 @@ CLI的一些功能需要登录到abp.io平台. 使用你的用户名登录
abp login <username> abp login <username>
``` ```
```bash
abp login <username> -p <password>
```
请注意,新的登录将终止先前的会话并创建一个新的会话. 请注意,新的登录将终止先前的会话并创建一个新的会话.
### logout ### logout

9
docs/zh-Hans/Getting-Started-Angular-Template.md

@ -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. -->

2
docs/zh-Hans/Getting-Started-AspNetCore-Application.md

@ -2,7 +2,7 @@
本教程将介绍如何开始以最少的依赖关系开始使用ABP开发. 本教程将介绍如何开始以最少的依赖关系开始使用ABP开发.
通常情况下你需要下载一个 ***[启动模板](https://abp.io/Templates)*** 通常情况下你需要下载一个 ***[启动模板](Getting-Started-AspNetCore-MVC-Template.md)***
### 创建一个新项目 ### 创建一个新项目

104
docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md

@ -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: <!-- TODO: this document has been moved, it should be deleted in the future. -->
````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**中打开解决方案:
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v3.png)
该解决方案具有分层结构(基于[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`项目并选择 **设置为启动项目**:
![set-as-startup-project](images/set-as-startup-project.png)
按F5(或Ctrl + F5)运行应用程序. 它将具有如下所示的输出:
![set-as-startup-project](images/db-migrator-app.png)
#### 使用EF Core Update-Database命令
Ef Core具有`Update-Database`命令, 可根据需要创建数据库并应用挂起的迁移. 右键单击`.Web`项目并选择**设置为启动项目**:
![set-as-startup-project](images/set-as-startup-project.png)
打开**包管理器控制台(Package Manager Console)**, 选择`.EntityFrameworkCore.DbMigrations`项目作为**默认项目**并运行`Update-Database`命令:
![pcm-update-database](images/pcm-update-database-v2.png)
这将基于配置的连接字符串创建新数据库.
> 使用`.Migrator`工具是建议的方法, 因为它还能初始化初始数据能够正确运行Web应用程序.
### 运行应用程序
你现在可以运行应用程序,它将会打开**home**页面:
![bookstore-homepage](images/bookstore-homepage.png)
单击 **登录** 按钮, 输入用户名`admin`, 密码`1q2w3E*`, 登录应用程序.
启动模板包括**身份管理**和**租户管理**模块. 登录后,将显示"管理"菜单, 你可以在其中管理**租户**,**角色**,**用户**和**权限**. 用户管理页面如下所示:
![bookstore-user-management](images/bookstore-user-management-v2.png)
### 下一步是什么?
* [应用程序开发教程](Tutorials/AspNetCore-Mvc/Part-I.md)

8
docs/zh-Hans/Getting-Started-With-Startup-Templates.md

@ -1,6 +1,8 @@
# 启动模板入门 # 启动模板入门
参阅下面的教程来学习如何开始使用的ABP框架预构建的应用程序启动模板: 请参阅以下教程,了解如何使用预构建的应用程序启动模板开始使用ABP框架:
* [ASP.NET Core MVC/Razor页面模板入门](Getting-Started-AspNetCore-MVC-Template.md) * [ASP.NET Core MVC / Razor Pages UI 入门](Getting-Started?UI=MVC&DB=EF&Tiered=No)
* [Angular UI模板入门](Getting-Started-Angular-Template.md) * [Angular UI 入门](Getting-Started?UI=NG&DB=EF&Tiered=No)
<!-- TODO: this document has been moved, it should be deleted in the future. -->

411
docs/zh-Hans/Getting-Started.md

@ -0,0 +1,411 @@
## 入门
````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/)
> 你可以也使用其他支持.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" }}
创建项目后你会有以下解决方案目录和文件:
![](images/solution-files-mvc.png)
在Visual Studio中打开 `.sln` 文件时,将看到以下解决方案结构:
{{if DB == "Mongo"}}
![vs-default-app-solution-structure](images/vs-app-solution-structure-mongodb.png)
{{else}}
![vs-default-app-solution-structure](images/vs-app-solution-structure{{if Tiered == "Yes"}}-tiered{{end}}.png)
{{end}}
{{ else if UI == "NG" }}
在创建的解决方案中有三个文件夹:
![](images/solution-files-non-mvc.png)
* `angular` 文件夹包含Angular UI应用程序.
* `aspnet-core` 文件夹包含后端应用程序.
* `react-native` 文件夹包含React Native UI 应用程序.
打开 `aspnet-core` 文件夹下的 `.sln`(`Visual Studio`解决方案)文件:
![vs-angular-app-backend-solution-structure](images/vs-spa-app-backend-structure{{if DB == "Mongo"}}-mongodb{{end}}.png)
{{ 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`项目并选择 **设置为启动项目**:
![set-as-startup-project](images/set-as-startup-project.png)
按F5(或Ctrl + F5)运行应用程序. 它将具有如下所示的输出:
![set-as-startup-project](images/db-migrator-app.png)
#### 使用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 }}
![set-as-startup-project](images/set-as-startup-project.png)
打开**包管理器控制台(Package Manager Console)**, 选择`.EntityFrameworkCore.DbMigrations`项目作为**默认项目**并运行`Update-Database`命令:
![package-manager-console-update-database](images/package-manager-console-update-database.png)
这将基于配置的连接字符串创建新数据库.
> 使用`.Migrator`工具是建议的方法, 因为它还能初始化初始数据能够正确运行Web应用程序.
{{ else if DB == "Mongo" }}
````json
"ConnectionStrings": {
"Default": "mongodb://localhost:27017/BookStore"
}
````
该解决方案被配置为在你的本地计算机中使用 **MongoDB**,因此你需要启动并运行一个MongoDB服务器实例或者将连接字符串更改为另一个MongoDB服务器.
### 初始化种子数据
该解决方案附带一个 `.DbMigrator` 控制台应用程序,该应用程序为初始数据提供了种子. 它对于开发以及生产环境都很有用.
> `.DbMigrator` 项目有自己的 `appsettings.json`.如果你更改了其他项目的 `appsettings.json`,也应该更改这个.
右键点击 `.DbMigrator` 并选择 **设置为启动项目**.
![set-as-startup-project](images/set-as-startup-project.png)
按F5(或Ctrl+F5)启动应用程序,你会看到以下输出:
![db-migrator-output](images/db-migrator-output.png)
> 数据库创建后会初始化种子数据, 其中包含用于登录的 `admin` 用户. 所以你至少使用 `.DbMigrator` 一次.
{{ end }}
### 运行应用程序
{{ if UI == "MVC" }}
{{ if Tiered == "Yes" }}
确保 `.IdentityServer` 是启动项目,运行应用程序后会在你的浏览器打开一个 **login** 页面.
> 在Visual Studio中使用Ctrl+F5(而不是F5)运行应用,如果你不用于调试,这会减少启动时间.
你可以登录,但是不能在这里进入主应用程序,它仅是验证服务器.
确保 `.HttpApi.Host` 是启动项目,运行应用程序后会在你的浏览器打开一个 **Swagger UI** 页面.
![swagger-ui](images/swagger-ui.png)
这里是Web应用程序使用的API应用程序.
最后确保 `.Web` 是启动项目,运行应用程序后会在你的浏览器打开一个 **welcome** 页面.
![mvc-tiered-app-home](images/bookstore-home.png)
点击 **login** 按钮重定向到 `Identity Server` 来登录应用程序.
![bookstore-login](images/bookstore-login.png)
{{ else }}
最后确保 `.Web` 是启动项目,运行应用程序后会在你的浏览器打开一个 **login** 页面.
> 在Visual Studio中使用Ctrl+F5(而不是F5)运行应用,如果你不用于调试,这会减少启动时间.
![bookstore-login](images/bookstore-login.png)
{{ 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 }}
![swagger-ui](images/swagger-ui.png)
你可以看到应用程序的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/).
![bookstore-login](images/bookstore-login.png)
{{ end }}
输入用户名 **admin**,密码 **1q2w3E*** 登录到应用程序.
![bookstore-home](images/bookstore-home.png)
应用程序已经启动并执行,你可以基于该启动模板开发应用程序.
#### 移动开发
ABP平台提供了[React Native](https://reactnative.dev/)模板用于开发移动应用程序.
> 该解决方案默认 `react-native` 包含了React Native应用程序,如果你不计划使用React Native开发移动应用程序,你可以忽略并删除 `react-native` 文件夹.
运行在Android模拟器或真机上的React Native应用程序无法连接到 `localhost` 上的后.要修复此问题,需要在本地IP上运行后端.
{{ if Tiered == "No"}}
![React Native host project local IP entry](images/rn-host-local-ip.png)
* 打开 `.HttpApi.Host` 文件夹下的 `appsettings.json` 文件. 将 `SelfUrl` 和 `Authority` 属性的 `localhost` 替换为你本地的IP地址.
* 打开 `.HttpApi.Host/Properties` 文件夹下的 `launchSettings.json` 文件. 将 `applicationUrl` 属性的 `localhost` 替换为你本地的IP地址.
{{ else if Tiered == "Yes" }}
![React Native tiered project local IP entry](images/rn-tiered-local-ip.png)
* 打开 `.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地址:
![react native environment local IP](images/rn-environment-local-ip.png)
{{ 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-interface](images/rn-expo-interface.png)
在上面的管理界面中,可以通过使用[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/).
![React Native login screen on iPhone 11](images/rn-login-iphone.png)
输入用户名 **admin**,密码 **1q2w3E*** 登录到应用程序.
应用程序已经启动并执行,你可以基于该启动模板开发应用程序.
> [应用程序启动模板](Startup-Templates/Application.md) 包含租户管理和Identity模块.
## 下一步是什么?
[应用程序开发教程](Tutorials/Part-1.md)

BIN
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-book-list-2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-book-list.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-template.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files-v2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

BIN
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-new-book-button.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

BIN
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration-v2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

BIN
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-swagger.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

1078
docs/zh-Hans/Tutorials/Part-1.md

File diff suppressed because it is too large

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

@ -0,0 +1 @@
TODO ....

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

@ -0,0 +1 @@
TODO ....

BIN
docs/zh-Hans/Tutorials/images/bookstore-actions-buttons.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog-v2.png → docs/zh-Hans/Tutorials/images/bookstore-add-create-dialog-v2.png

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-edit-dialog.png → docs/zh-Hans/Tutorials/images/bookstore-add-edit-dialog.png

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page-v2.png → docs/zh-Hans/Tutorials/images/bookstore-add-index-page-v2.png

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-angular-file-tree.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-appservice-tests.png → docs/zh-Hans/Tutorials/images/bookstore-appservice-tests.png

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-book-list-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-book-list.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-books-table-actions.png → docs/zh-Hans/Tutorials/images/bookstore-books-table-actions.png

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-books-table.png → docs/zh-Hans/Tutorials/images/bookstore-books-table.png

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-confirmation-popup.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-dialog-2.png → docs/zh-Hans/Tutorials/images/bookstore-create-dialog-2.png

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-dialog.png → docs/zh-Hans/Tutorials/images/bookstore-create-dialog.png

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-create-project-angular.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-create-project-mvc.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-creating-book-list-terminal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-creating-books-module-terminal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-database-tables-ef.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-database-tables-mongodb.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-edit-button.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-empty-new-book-modal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-final-actions-dropdown.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-generate-state-books.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-homepage.png → docs/zh-Hans/Tutorials/images/bookstore-homepage.png

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file-v2.png → docs/zh-Hans/Tutorials/images/bookstore-index-js-file-v2.png

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-initial-book-list-page.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-initial-books-page-with-layout.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-localization-files-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-menu-items.png → docs/zh-Hans/Tutorials/images/bookstore-menu-items.png

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-migrations-applied-angular.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-migrations-applied-mvc.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-new-book-button.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-new-book-form-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-new-book-form.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-new-menu-item.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-open-package-manager-console.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-pmc-add-book-migration-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration.png → docs/zh-Hans/Tutorials/images/bookstore-pmc-add-book-migration.png

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-service-terminal-output.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-solution-structure-angular.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v3.png → docs/zh-Hans/Tutorials/images/bookstore-solution-structure-mvc.png

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-start-project-angular.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-start-project-mvc.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-swagger-book-dto-properties.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-swagger.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 810 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-js-proxy-getlist-network.png → docs/zh-Hans/Tutorials/images/bookstore-test-js-proxy-getlist-network.png

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-js-proxy-getlist.png → docs/zh-Hans/Tutorials/images/bookstore-test-js-proxy-getlist.png

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-test-projects-angular.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects-v2.png → docs/zh-Hans/Tutorials/images/bookstore-test-projects-mvc.png

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-test-projects-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-update-database-after-book-entity.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-user-management.png → docs/zh-Hans/Tutorials/images/bookstore-user-management.png

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-visual-studio-solution-v3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/zh-Hans/Tutorials/images/generate-proxy-command.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
docs/zh-Hans/Tutorials/images/generated-proxies.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

BIN
docs/zh-Hans/Tutorials/images/mozilla-self-signed-cert-error.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

181
docs/zh-Hans/UI/Angular/Confirmation-Service.md

@ -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` 是标题本地化的插值参数.
使用以上选项确认弹层窗口如下所示:
![confirmation](./images/confirmation.png)
你可以传递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)

2
docs/zh-Hans/UI/Angular/Permission-Management.md

@ -76,4 +76,4 @@ const routes: Routes = [
## 下一步是什么? ## 下一步是什么?
* [Config State](./Config-State.md) * [确认弹层](./Confirmation-Service.md)

155
docs/zh-Hans/UI/Angular/Toaster-Service.md

@ -0,0 +1,155 @@
# Toast Overlay
你可以通常将@abp/ng.theme.shared包提供的 `ToasterService` 放置在你项目的根级别下以覆盖显示消息.
## 入门
你不必在模块或组件级别提供 `ToasterService`,它已经在**根**级别提供,你可以在你的组件,指令或服务直接注入并使用它.
```js
import { ToasterService } from '@abp/ng.theme.shared';
@Component({
/* class metadata here */
})
class DemoComponent {
constructor(private toaster: ToasterService) {}
}
```
## 用法
你可以使用 `ToasterService` 的 `success`, `warn`, `error` 和 `info` 方法显示一个overlay.
### 如何显示一个Toast Overlay
```js
this.toast.success('Message', 'Title');
```
- `ToasterService` 方法接收三个参数,分别是 `message`, `title`, 和 `options`.
- `success`, `warn`, `error`, 和 `info` 方法返回一个已打开的 toast overlay Id. 可以使用此id删除toast.
### 如何显示具有给定选项的Toast Overlay
选项可以作为第三个参数传递给`success`, `warn`, `error`, 和 `info` 方法:
```js
import { Toaster, ToasterService } from '@abp/ng.theme.shared';
//...
constructor(private toaster: ToasterService) {}
//...
const options: Partial<Toaster.ToastOptions> = {
life: 10000,
sticky: false,
closable: true,
tapToDismiss: true,
messageLocalizationParams: ['Demo', '1'],
titleLocalizationParams: []
};
this.toaster.error('AbpUi::EntityNotFoundErrorMessage', 'AbpUi::Error', options);
```
- `life` 选项是关闭的时间毫秒数. 默认值是 `5000`.
- `sticky` 选项为 `true` 时忽略 `life` 选项,将toast overlay留在屏幕上. 默认值是 `false`.
- `closable` 选项为 `true` 时在toast overlay上显示关闭图标. 默认值是 `true`.
- `tapToDismiss` 选项为 `true` 允许通过单击关闭toast overlay. 默认值是 `false`.
- `yesText` 是确定按钮的文本,可以传递本地化键或本地化对象. 默认值是 `AbpUi::Yes`.
- `messageLocalizationParams` 是用于消息本地化的插值参数.
- `titleLocalizationParams` 是标题本地化的插值参数.
使用上面的选项,toast overlay看起来像这样:
![toast](./images/toast.png)
### 如何删除 Toast Overlay
已打开的toast overlay可以通过手动调用 `remove` 方法传递指定的 toast `id`删除.
```js
const toastId = this.toast.success('Message', 'Title')
this.toast.remove(toastId);
```
### 如何删除所有的Toasts
可以手动调用 `clear` 方法删除所有的已打开的toasts.
```js
this.toast.clear();
```
## API
### success
```js
success(
message: Config.LocalizationParam,
title: Config.LocalizationParam,
options?: Partial<Toaster.ToastOptions>,
): number
```
- `Config` 命令空间可以从 `@abp/ng.core` 导入.
- `Toaster` 命令空间可以从 `@abp/ng.theme.shared` 导入.
> 请参见[`Config.LocalizationParam`类型](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/core/src/lib/models/config.ts#L46)和[`Toaster` namespace](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/theme-shared/src/lib/models/toaster.ts)
### warn
```js
warn(
message: Config.LocalizationParam,
title: Config.LocalizationParam,
options?: Partial<Toaster.ToastOptions>,
): number
```
### error
```js
error(
message: Config.LocalizationParam,
title: Config.LocalizationParam,
options?: Partial<Toaster.ToastOptions>,
): number
```
### info
```js
info(
message: Config.LocalizationParam,
title: Config.LocalizationParam,
options?: Partial<Toaster.ToastOptions>,
): number
```
### remove
```js
remove(id: number): void
```
按给定的id移除打开的toast.
### clear
```js
clear(): void
```
删除所有打开的toasts.
## 另请参阅
- [Confirmation Popup](./Confirmation-Service.md)
## 确认弹层?
- [Config State](./Config-State.md)

BIN
docs/zh-Hans/UI/Angular/images/confirmation.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
docs/zh-Hans/UI/Angular/images/toast.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

22
docs/zh-Hans/UI/Common/Utils/Linked-List.md

@ -1229,7 +1229,7 @@ find(predicate: ListIteratorFunction<T>): ListNode<T> | undefined
从链表中找到与给定谓词匹配的第一个节点: 从链表中找到与给定谓词匹配的第一个节点:
```js ```js
list.addTailMany(['a', 'b', 'b', 'c']); list.addManyTail(['a', 'b', 'b', 'c']);
// "a" <-> "b" <-> "b" <-> "c" // "a" <-> "b" <-> "b" <-> "c"
@ -1251,7 +1251,7 @@ findIndex(predicate: ListIteratorFunction<T>): number
从链表中找到与给定谓词匹配的第一个节点的位置: 从链表中找到与给定谓词匹配的第一个节点的位置:
```js ```js
list.addTailMany(['a', 'b', 'b', 'c']); list.addManyTail(['a', 'b', 'b', 'c']);
// "a" <-> "b" <-> "b" <-> "c" // "a" <-> "b" <-> "b" <-> "c"
@ -1279,7 +1279,7 @@ get(position: number): ListNode<T> | undefined
查找并返回链表中特定位置的节点: 查找并返回链表中特定位置的节点:
```js ```js
list.addTailMany(['a', 'b', 'c']); list.addManyTail(['a', 'b', 'c']);
// "a" <-> "b" <-> "c" // "a" <-> "b" <-> "c"
@ -1303,7 +1303,7 @@ indexOf(value: T, compareFn?: ListComparisonFn<T>): number
在链表中找到匹配给定值的第一个节点位置: 在链表中找到匹配给定值的第一个节点位置:
```js ```js
list.addTailMany(['a', 'b', 'b', 'c']); list.addManyTail(['a', 'b', 'b', 'c']);
// "a" <-> "b" <-> "b" <-> "c" // "a" <-> "b" <-> "b" <-> "c"
@ -1325,7 +1325,7 @@ i3 === -1
你可以自定义比较器 你可以自定义比较器
```js ```js
list.addTailMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]); list.addManyTail([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]);
// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3}
@ -1365,7 +1365,7 @@ forEach(iteratorFn: ListIteratorFn<T>): void
从头到尾在链表中的所有节点上运行回调函数: 从头到尾在链表中的所有节点上运行回调函数:
```js ```js
list.addTailMany(['a', 'b', 'c']); list.addManyTail(['a', 'b', 'c']);
// "a" <-> "b" <-> "c" // "a" <-> "b" <-> "c"
@ -1381,7 +1381,7 @@ list.forEach((node, index) => console.log(node.value + index));
链表是可迭代的. 换句话说你可以使用诸如`for ... of`之类的方法. 链表是可迭代的. 换句话说你可以使用诸如`for ... of`之类的方法.
```js ```js
list.addTailMany(['a', 'b', 'c']); list.addManyTail(['a', 'b', 'c']);
// "a" <-> "b" <-> "c" // "a" <-> "b" <-> "c"
@ -1405,7 +1405,7 @@ toArray(): T[]
转换链表值为数组: 转换链表值为数组:
```js ```js
list.addTailMany(['a', 'b', 'c']); list.addManyTail(['a', 'b', 'c']);
// "a" <-> "b" <-> "c" // "a" <-> "b" <-> "c"
@ -1427,7 +1427,7 @@ toNodeArray(): T[]
转换链表节点为数组: 转换链表节点为数组:
```js ```js
list.addTailMany(['a', 'b', 'c']); list.addManyTail(['a', 'b', 'c']);
// "a" <-> "b" <-> "c" // "a" <-> "b" <-> "c"
@ -1449,7 +1449,7 @@ toString(mapperFn: ListMapperFn<T> = JSON.stringify): string
将链表转换为节点及其关系的字符串表示形式: 将链表转换为节点及其关系的字符串表示形式:
```js ```js
list.addTailMany(['a', 2, 'c', { k: 4, v: 'd' }]); list.addManyTail(['a', 2, 'c', { k: 4, v: 'd' }]);
// "a" <-> 2 <-> "c" <-> {"k":4,"v":"d"} // "a" <-> 2 <-> "c" <-> {"k":4,"v":"d"}
@ -1507,7 +1507,7 @@ export class ListNode<T = any> {
- `previous`引用列表中的上一个节点. - `previous`引用列表中的上一个节点.
```js ```js
list.addTailMany([ 0, 1, 2 ]); list.addManyTail([ 0, 1, 2 ]);
console.log( console.log(
list.head.value, // 0 list.head.value, // 0

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

@ -5,13 +5,7 @@
"items": [ "items": [
{ {
"text": "从启动模板开始", "text": "从启动模板开始",
"path": "Getting-Started-With-Startup-Templates.md", "path": "Getting-Started.md"
"items": [
{
"text": "ASP.NET Core MVC 模板",
"path": "Getting-Started-AspNetCore-MVC-Template.md"
}
]
}, },
{ {
"text": "从空项目开始", "text": "从空项目开始",
@ -218,8 +212,11 @@
}, },
{ {
"text": "领域驱动设计", "text": "领域驱动设计",
"path": "Domain-Driven-Design.md",
"items": [ "items": [
{
"text": "概述",
"path": "Domain-Driven-Design.md"
},
{ {
"text": "领域层", "text": "领域层",
"items": [ "items": [
@ -324,6 +321,14 @@
"text": "权限管理", "text": "权限管理",
"path": "UI/Angular/Permission-Management.md" "path": "UI/Angular/Permission-Management.md"
}, },
{
"text": "确认弹层",
"path": "UI/Angular/Confirmation-Service.md"
},
{
"text": "Toast Overlay",
"path": "UI/Angular/Toaster-Service.md"
},
{ {
"text": "配置状态", "text": "配置状态",
"path": "UI/Angular/Config-State.md" "path": "UI/Angular/Config-State.md"
@ -372,8 +377,11 @@
}, },
{ {
"text": "数据访问", "text": "数据访问",
"path":"Data-Access.md",
"items": [ "items": [
{
"text": "概述",
"path": "Data-Access.md"
},
{ {
"text": "连接字符串", "text": "连接字符串",
"path": "Connection-Strings.md" "path": "Connection-Strings.md"
@ -457,8 +465,11 @@
}, },
{ {
"text": "启动模板", "text": "启动模板",
"path": "Startup-Templates/Index.md",
"items": [ "items": [
{
"text": "概述",
"path": "Startup-Templates/Index.md"
},
{ {
"text": "应用程序", "text": "应用程序",
"path": "Startup-Templates/Application.md" "path": "Startup-Templates/Application.md"

28
docs/zh-Hans/docs-params.json

@ -0,0 +1,28 @@
{
"parameters": [
{
"name": "UI",
"displayName": "UI",
"values": {
"MVC": "MVC / Razor Pages",
"NG": "Angular"
}
},
{
"name": "DB",
"displayName": "Database",
"values": {
"EF": "Entity Framework Core",
"Mongo": "MongoDB"
}
},
{
"name": "Tiered",
"displayName": "Tiered",
"values": {
"No": "Not Tiered",
"Yes": "Tiered"
}
}
]
}

16
framework/Volo.Abp.sln

@ -277,7 +277,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Http.Client.Identi
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.ObjectExtending", "src\Volo.Abp.ObjectExtending\Volo.Abp.ObjectExtending.csproj", "{D1815C77-16D6-4F99-8814-69065CD89FB3}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.ObjectExtending", "src\Volo.Abp.ObjectExtending\Volo.Abp.ObjectExtending.csproj", "{D1815C77-16D6-4F99-8814-69065CD89FB3}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.ObjectExtending.Tests", "test\Volo.Abp.ObjectExtending.Tests\Volo.Abp.ObjectExtending.Tests.csproj", "{17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.ObjectExtending.Tests", "test\Volo.Abp.ObjectExtending.Tests\Volo.Abp.ObjectExtending.Tests.csproj", "{17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TextTemplating", "src\Volo.Abp.TextTemplating\Volo.Abp.TextTemplating.csproj", "{9E53F91F-EACD-4191-A487-E727741F1311}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TextTemplating.Tests", "test\Volo.Abp.TextTemplating.Tests\Volo.Abp.TextTemplating.Tests.csproj", "{251C7FD3-D313-4BCE-8068-352EC7EEA275}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -829,6 +833,14 @@ Global
{17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}.Debug|Any CPU.Build.0 = Debug|Any CPU {17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}.Release|Any CPU.ActiveCfg = Release|Any CPU {17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}.Release|Any CPU.Build.0 = Release|Any CPU {17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}.Release|Any CPU.Build.0 = Release|Any CPU
{9E53F91F-EACD-4191-A487-E727741F1311}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E53F91F-EACD-4191-A487-E727741F1311}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E53F91F-EACD-4191-A487-E727741F1311}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E53F91F-EACD-4191-A487-E727741F1311}.Release|Any CPU.Build.0 = Release|Any CPU
{251C7FD3-D313-4BCE-8068-352EC7EEA275}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{251C7FD3-D313-4BCE-8068-352EC7EEA275}.Debug|Any CPU.Build.0 = Debug|Any CPU
{251C7FD3-D313-4BCE-8068-352EC7EEA275}.Release|Any CPU.ActiveCfg = Release|Any CPU
{251C7FD3-D313-4BCE-8068-352EC7EEA275}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -970,6 +982,8 @@ Global
{E1963439-2BE5-4DB5-8438-2A9A792A1ADA} = {447C8A77-E5F0-4538-8687-7383196D04EA} {E1963439-2BE5-4DB5-8438-2A9A792A1ADA} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{D1815C77-16D6-4F99-8814-69065CD89FB3} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {D1815C77-16D6-4F99-8814-69065CD89FB3} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5} = {447C8A77-E5F0-4538-8687-7383196D04EA} {17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{9E53F91F-EACD-4191-A487-E727741F1311} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{251C7FD3-D313-4BCE-8068-352EC7EEA275} = {447C8A77-E5F0-4538-8687-7383196D04EA}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

12
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs

@ -43,6 +43,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
await ProcessFieldsAsync(context, output); await ProcessFieldsAsync(context, output);
RemoveFormGroupItemsNotInModel(context, output, list);
SetContent(context, output, list, childContent); SetContent(context, output, list, childContent);
SetFormAttributes(context, output); SetFormAttributes(context, output);
@ -143,6 +145,14 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
} }
} }
protected virtual void RemoveFormGroupItemsNotInModel(TagHelperContext context, TagHelperOutput output, List<FormGroupItem> items)
{
var models = GetModels(context, output);
items.RemoveAll(x => models.All(m => !m.Name.Equals(x.PropertyName, StringComparison.InvariantCultureIgnoreCase)));
}
protected virtual async Task ProcessSelectGroupAsync(TagHelperContext context, TagHelperOutput output, ModelExpression model) protected virtual async Task ProcessSelectGroupAsync(TagHelperContext context, TagHelperOutput output, ModelExpression model)
{ {
var abpSelectTagHelper = GetSelectGroupTagHelper(context, output, model); var abpSelectTagHelper = GetSelectGroupTagHelper(context, output, model);
@ -152,7 +162,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
protected virtual AbpTagHelper GetSelectGroupTagHelper(TagHelperContext context, TagHelperOutput output, ModelExpression model) protected virtual AbpTagHelper GetSelectGroupTagHelper(TagHelperContext context, TagHelperOutput output, ModelExpression model)
{ {
return IsRadioGroup(model.ModelExplorer) ? return IsRadioGroup(model.ModelExplorer) ?
GetAbpRadioInputTagHelper(model) : GetAbpRadioInputTagHelper(model) :
GetSelectTagHelper(model); GetSelectTagHelper(model);
} }

11
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs

@ -58,7 +58,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
protected virtual async Task<(string, bool)> GetFormInputGroupAsHtmlAsync(TagHelperContext context, TagHelperOutput output) protected virtual async Task<(string, bool)> GetFormInputGroupAsHtmlAsync(TagHelperContext context, TagHelperOutput output)
{ {
var (inputTag, isCheckBox) = await GetInputTagHelperOutputAsync(context, output); var (inputTag, isCheckBox) = await GetInputTagHelperOutputAsync(context, output);
var inputHtml = inputTag.Render(_encoder); var inputHtml = inputTag.Render(_encoder);
var label = await GetLabelAsHtmlAsync(context, output, inputTag, isCheckBox); var label = await GetLabelAsHtmlAsync(context, output, inputTag, isCheckBox);
var info = GetInfoAsHtml(context, output, inputTag, isCheckBox); var info = GetInfoAsHtml(context, output, inputTag, isCheckBox);
@ -162,7 +162,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
protected virtual void AddDisabledAttribute(TagHelperOutput inputTagHelperOutput) protected virtual void AddDisabledAttribute(TagHelperOutput inputTagHelperOutput)
{ {
if (inputTagHelperOutput.Attributes.ContainsName("disabled") == false && if (inputTagHelperOutput.Attributes.ContainsName("disabled") == false &&
(TagHelper.IsDisabled || TagHelper.AspFor.ModelExplorer.GetAttribute<DisabledInput>() != null)) (TagHelper.IsDisabled || TagHelper.AspFor.ModelExplorer.GetAttribute<DisabledInput>() != null))
{ {
inputTagHelperOutput.Attributes.Add("disabled", ""); inputTagHelperOutput.Attributes.Add("disabled", "");
@ -171,7 +171,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
protected virtual void AddReadOnlyAttribute(TagHelperOutput inputTagHelperOutput) protected virtual void AddReadOnlyAttribute(TagHelperOutput inputTagHelperOutput)
{ {
if (inputTagHelperOutput.Attributes.ContainsName("readonly") == false && if (inputTagHelperOutput.Attributes.ContainsName("readonly") == false &&
(TagHelper.IsReadonly != false || TagHelper.AspFor.ModelExplorer.GetAttribute<ReadOnlyInput>() != null)) (TagHelper.IsReadonly != false || TagHelper.AspFor.ModelExplorer.GetAttribute<ReadOnlyInput>() != null))
{ {
inputTagHelperOutput.Attributes.Add("readonly", ""); inputTagHelperOutput.Attributes.Add("readonly", "");
@ -407,9 +407,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
list.Add(new FormGroupItem list.Add(new FormGroupItem
{ {
HtmlContent = html, HtmlContent = html,
Order = order Order = order,
PropertyName = propertyName
}); });
} }
} }
} }
} }

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save