Browse Source

Merge branch 'dev' of https://github.com/kgamalseif/abp into dev

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

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

@ -13,10 +13,11 @@
"Permission:Edit": "Edit",
"Permission:Delete": "Delete",
"Permission:Create": "Create",
"Permission:Accounting": "Accounting",
"Permission:Accounting:Quotation": "Quotation",
"Permission:Accounting": "Accounting",
"Permission:Accounting:Quotation": "Quotation",
"Permission:Accounting:Invoice": "Invoice",
"Menu:Organizations": "Organizations",
"Menu:Accounting": "Accounting",
"Menu:Accounting": "Accounting",
"Menu:Packages": "Packages",
"NpmPackageDeletionWarningMessage": "This NPM Package will be deleted. Do you confirm that?",
"NugetPackageDeletionWarningMessage": "This Nuget Package will be deleted. Do you confirm that?",
@ -103,8 +104,21 @@
"Price": "Price",
"DiscountText": "Discount text",
"DiscountQuantity": "Discount quantity",
"DiscountPrice": "Discount price",
"DiscountPrice": "Discount price",
"Quotation": "Quotation",
"Generate": "Generate"
"ExtraText": "Extra Text",
"ExtraAmount": "Extra Amount",
"DownloadQuotation": "Download Quotation",
"Invoice": "Invoice",
"TaxNumber": "Tax Number",
"InvoiceNumber": "Invoice Number",
"InvoiceDate": "Invoice Date",
"Quantity": "Quantity",
"AddProduct": "Add Product",
"AddProductWarning": "You need to add product!",
"TotalPrice": "Total Price",
"Generate": "Generate",
"MissingQuantityField": "The quantity field is required!",
"MissingPriceField": "The Price field is required!"
}
}

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/=Mutable/@EntryIndexedValue">False</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>
</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.
* `--npm`: Only updates NPM 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
@ -170,7 +172,11 @@ Some features of the CLI requires to be logged in to abp.io platform. To login w
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

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 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?
[Application development tutorial](tutorials/book-store/part-1.md)
[Application development tutorial](Tutorials/Part-1.md)

52
docs/en/Localization.md

@ -87,6 +87,21 @@ A JSON localization file content is shown below:
* Every localization file should define the `culture` code for the file (like "en" or "en-US").
* `texts` section just contains key-value collection of the localization strings (keys may have spaces too).
### Default Resource
`AbpLocalizationOptions.DefaultResourceType` can be set to a resource type, so it is used when the localization resource was not specified:
````csharp
Configure<AbpLocalizationOptions>(options =>
{
options.DefaultResourceType = typeof(TestResource);
});
````
> The [application startup template](Startup-Templates/Application.md) sets `DefaultResourceType` to the localization resource of the application.
See the *Client Side* section below for a use case.
### Short Localization Resource Name
Localization resources are also available in the client (JavaScript) side. So, setting a short name for the localization resource makes it easy to use localization texts. Example:
@ -166,6 +181,10 @@ public class MyService
}
````
##### Format Arguments
Format arguments can be passed after the localization key. If your message is `Hello {0}, welcome!`, then you can pass the `{0}` argument to the localizer like `_localizer["HelloMessage", "John"]`
#### Simplest Usage In A Razor View/Page
````c#
@ -180,18 +199,47 @@ Refer to the [Microsoft's localization documentation](https://docs.microsoft.com
ABP provides JavaScript services to use the same localized texts in the client side.
Get a localization resource:
#### getResource
`abp.localization.getResource` function is used to get a localization resource:
````js
var testResource = abp.localization.getResource('Test');
````
Localize a string:
Then you can localize a string based on this resource:
````js
var str = testResource('HelloWorld');
````
#### localize
`abp.localization.localize` function is a shortcut where you can both specify the text name and the resource name:
````js
var str = abp.localization.localize('HelloWorld', 'Test');
````
`HelloWorld` is the text to localize, where `Test` is the localization resource name here.
If you don't specify the localization resource name, it uses the default localization resource defined on the `AbpLocalizationOptions` (see the *Default Resource* section above). Example:
````js
var str = abp.localization.localize('HelloWorld'); //uses the default resource
````
##### Format Arguments
If your localized string contains arguments, like `Hello {0}, welcome!`, you can pass arguments to the localization methods. Examples:
````js
var str1 = abp.localization.getResource('Test')('HelloWelcomeMessage', 'John');
var str2 = abp.localization.localize('HelloWorld', 'Test', 'John');
````
Both of the samples above produce the output `Hello John, welcome!`.
## See Also
* [Localization in Angular UI](UI/Angular/Localization.md)

8
docs/en/Object-Extensions.md

@ -197,8 +197,8 @@ ObjectExtensionManager.Instance
"SocialSecurityNumber",
options =>
{
options.ValidationAttributes.Add(new RequiredAttribute());
options.ValidationAttributes.Add(
options.Attributes.Add(new RequiredAttribute());
options.Attributes.Add(
new StringLengthAttribute(32) {
MinimumLength = 6
}
@ -248,12 +248,12 @@ ObjectExtensionManager.Instance
objConfig.AddOrUpdateProperty<string>("Password", propertyConfig =>
{
propertyConfig.ValidationAttributes.Add(new RequiredAttribute());
propertyConfig.Attributes.Add(new RequiredAttribute());
});
objConfig.AddOrUpdateProperty<string>("PasswordRepeat", propertyConfig =>
{
propertyConfig.ValidationAttributes.Add(new RequiredAttribute());
propertyConfig.Attributes.Add(new RequiredAttribute());
});
//Write a common validation logic works on multiple properties

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

@ -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}}
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)
@ -335,7 +335,7 @@ INSERT INTO AppBooks (Id,CreationTime,[Name],[Type],PublishDate,Price) VALUES
### 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` 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`:
![Initial book list page](./images/bookstore-generate-state-books.png)
```bash
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.
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
@ -885,32 +885,6 @@ Now, you can open your browser to see the changes:
#### 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:
```js
@ -993,6 +967,32 @@ export class BookListComponent implements OnInit {
* We imported `CreateUpdateBook`.
* 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:
![Save button to the modal](./images/bookstore-new-book-form-v2.png)

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

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

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",
invisible: false,
order: 2,
requiredPolicy: "MyProjectName::MyNewPage"
requiredPolicy: "MyProjectName.MyNewPage"
};
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:**
```js
import { eIdentityRouteNames } from '@abp/ng.identity';
// this.config is instance of ConfigStateService
const newRoute: ABP.Route = {
parentName: "AbpAccount::Login",
parentName: eIdentityRouteNames.IdentityManagement,
name: "My New Page",
iconClass: "fa fa-dashboard",
path: "page",
invisible: false,
order: 2,
requiredPolicy: "MyProjectName::MyNewPage"
requiredPolicy: "MyProjectName.MyNewPage"
};
this.config.dispatchAddRoute(newRoute);
// 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
@ -291,4 +293,4 @@ Please refer to `Config.Environment` type for all the properties you can pass to
## 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)
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
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)

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:
```js
list.addTailMany(['a', 'b', 'b', 'c']);
list.addManyTail(['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:
```js
list.addTailMany(['a', 'b', 'b', 'c']);
list.addManyTail(['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:
```js
list.addTailMany(['a', 'b', 'c']);
list.addManyTail(['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:
```js
list.addTailMany(['a', 'b', 'b', 'c']);
list.addManyTail(['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:
```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}
@ -1408,7 +1408,7 @@ forEach(iteratorFn: ListIteratorFn<T>): void
Runs a function on all nodes in a linked list from head to tail:
```js
list.addTailMany(['a', 'b', 'c']);
list.addManyTail(['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.
```js
list.addTailMany(['a', 'b', 'c']);
list.addManyTail(['a', 'b', 'c']);
// "a" <-> "b" <-> "c"
@ -1449,7 +1449,7 @@ toArray(): T[]
Converts a linked list to an array of values:
```js
list.addTailMany(['a', 'b', 'c']);
list.addManyTail(['a', 'b', 'c']);
// "a" <-> "b" <-> "c"
@ -1471,7 +1471,7 @@ toNodeArray(): ListNode<T>[]
Converts a linked list to an array of nodes:
```js
list.addTailMany(['a', 'b', 'c']);
list.addManyTail(['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:
```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"}
@ -1562,7 +1562,7 @@ export class ListNode<T = any> {
- `previous` refers to the previous node in the list.
```js
list.addTailMany([ 0, 1, 2 ]);
list.addManyTail([ 0, 1, 2 ]);
console.log(
list.head.value, // 0

6
docs/en/docs-nav.json

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

6
docs/zh-Hans/CLI.md

@ -135,6 +135,8 @@ abp update [options]
* `--include-previews``-p`: 将预览版, 测试版本 和 rc 包 同时更新到最新版本.
* `--npm`: 仅更新NPM包
* `--nuget`: 仅更新的NuGet包
* `--solution-path``-sp`: 指定解决方案路径/目录. 默认使用当前目录
* `--solution-name``-sn`: 指定解决方案名称. 默认在目录中搜索`*.sln`文件.
### 切换到每晚构建(预览)包
@ -164,6 +166,10 @@ CLI的一些功能需要登录到abp.io平台. 使用你的用户名登录
abp login <username>
```
```bash
abp login <username> -p <password>
```
请注意,新的登录将终止先前的会话并创建一个新的会话.
### 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开发.
通常情况下你需要下载一个 ***[启动模板](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:
````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)
<!-- TODO: this document has been moved, it should be deleted in the future. -->

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)
* [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. -->

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

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

File diff suppressed because it is too large

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

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

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

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

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

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

@ -86,10 +86,32 @@ this.confirmation.warn(
- `messageLocalizationParams`是用于消息本地化的插值参数.
- `titleLocalizationParams` 是标题本地化的插值参数.
With the options above, the confirmation popup looks like this:
使用以上选项确认弹层窗口如下所示:
![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` 方法手动删除:

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

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

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

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

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

@ -5,13 +5,7 @@
"items": [
{
"text": "从启动模板开始",
"path": "Getting-Started-With-Startup-Templates.md",
"items": [
{
"text": "ASP.NET Core MVC 模板",
"path": "Getting-Started-AspNetCore-MVC-Template.md"
}
]
"path": "Getting-Started.md"
},
{
"text": "从空项目开始",
@ -339,6 +333,10 @@
"text": "配置状态",
"path": "UI/Angular/Config-State.md"
},
{
"text": "修改菜单",
"path": "UI/Angular/Modifying-the-Menu.md"
},
{
"text": "替换组件",
"path": "UI/Angular/Component-Replacement.md"

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

Loading…
Cancel
Save