mirror of https://github.com/abpframework/abp
				
				
				
			Merge branch 'dev' of https://github.com/abpframework/abp into dev
	
		
	
				
					
				
			
						commit
						b2e13444cd
					
				| @ -0,0 +1,114 @@ | ||||
| # Easy TrackByFunction Implementation | ||||
| 
 | ||||
| `TrackByService` is a utility service to provide an easy implementation for one of the most frequent needs in Angular templates: `TrackByFunction`. Please see [this page in Angular docs](https://angular.io/guide/template-syntax#ngfor-with-trackby) for its purpose. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## Getting Started | ||||
| 
 | ||||
| You do not have to provide the `TrackByService` at module or component level, because it is already **provided in root**. You can inject and start using it immediately in your components. For better type support, you may pass in the type of the iterated item to it. | ||||
| 
 | ||||
| ```js | ||||
| import { TrackByService } from '@abp/ng.core'; | ||||
| 
 | ||||
| @Component({ | ||||
|   /* class metadata here */ | ||||
| }) | ||||
| class DemoComponent { | ||||
|   list: Item[]; | ||||
| 
 | ||||
|   constructor(public readonly track: TrackByService<Item>) {} | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| > Noticed `track` is `public` and `readonly`? That is because we will see some examples where methods of `TrackByService` instance are directly called in the component's template. That may be considered as an anti-pattern, but it has its own advantage, especially when component inheritance is leveraged. You can always use public component properties instead. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| **The members are also exported as separate functions.** If you do not want to inject `TrackByService`, you can always import and use those functions directly in your classes. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## Usage | ||||
| 
 | ||||
| There are two approaches available. | ||||
| 
 | ||||
| 1. You may inject `TrackByService` to your component and use its members. | ||||
| 2. You may use exported higher-order functions directly on component properties. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ### How to Track Items by a Key | ||||
| 
 | ||||
| You can use `by` to get a `TrackByFunction` that tracks the iterated object based on one of its keys. For type support, you may pass in the type of the iterated item to it. | ||||
| 
 | ||||
| ```html | ||||
| <!-- template of DemoComponent --> | ||||
| 
 | ||||
| <div *ngFor="let item of list; trackBy: track.by('id')">{%{{{ item.name }}}%}</div> | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| `by` is exported as a stand-alone function and is named `trackBy`. | ||||
| 
 | ||||
| ```js | ||||
| import { trackBy } from "@abp/ng.core"; | ||||
| 
 | ||||
| @Component({ | ||||
|   template: ` | ||||
|     <div | ||||
|       *ngFor="let item of list; trackBy: trackById" | ||||
|     > | ||||
|       {%{{{ item.name }}}%} | ||||
|     </div> | ||||
|   `, | ||||
| }) | ||||
| class DemoComponent { | ||||
|   list: Item[]; | ||||
| 
 | ||||
|   trackById = trackBy<Item>('id'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ### How to Track by a Deeply Nested Key | ||||
| 
 | ||||
| You can use `byDeep` to get a `TrackByFunction` that tracks the iterated object based on a deeply nested key. For type support, you may pass in the type of the iterated item to it. | ||||
| 
 | ||||
| ```html | ||||
| <!-- template of DemoComponent --> | ||||
| 
 | ||||
| <div | ||||
|   *ngFor="let item of list; trackBy: track.byDeep('tenant', 'account', 'id')" | ||||
| > | ||||
|   {%{{{ item.tenant.name }}}%} | ||||
| </div> | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| `byDeep` is exported as a stand-alone function and is named `trackByDeep`. | ||||
| 
 | ||||
| ```js | ||||
| import { trackByDeep } from "@abp/ng.core"; | ||||
| 
 | ||||
| @Component({ | ||||
|   template: ` | ||||
|     <div | ||||
|       *ngFor="let item of list; trackBy: trackByTenantAccountId" | ||||
|     > | ||||
|       {%{{{ item.name }}}%} | ||||
|     </div> | ||||
|   `, | ||||
| }) | ||||
| class DemoComponent { | ||||
|   list: Item[]; | ||||
| 
 | ||||
|   trackByTenantAccountId = trackByDeep<Item>('tenant', 'account', 'id'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| @ -0,0 +1,3 @@ | ||||
| # ABP Datatables.Net Integration for ASP.NET Core UI | ||||
| 
 | ||||
| TODO | ||||
| @ -0,0 +1,92 @@ | ||||
| # Collapse | ||||
| 
 | ||||
| ## Introduction | ||||
| 
 | ||||
| `abp-collapse-body` is the main container for showing and hiding content. `abp-collapse-id` is used to show and hide the content container. Can be triggered with both `abp-button` and `a` tags. | ||||
| 
 | ||||
| Basic usage: | ||||
| 
 | ||||
| ````xml | ||||
| <abp-button button-type="Primary" abp-collapse-id="collapseExample" text="Button with data-target" /> | ||||
| <a abp-button="Primary" abp-collapse-id="collapseExample"> Link with href </a> | ||||
| 
 | ||||
| <abp-collapse-body id="collapseExample">        | ||||
|                     Anim pariatur wolf moon tempor,,, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. | ||||
| </abp-collapse-body> | ||||
| ```` | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## Demo | ||||
| 
 | ||||
| See the [collapse demo page](https://bootstrap-taghelpers.abp.io/Components/Collapse) to see it in action. | ||||
| 
 | ||||
| ## Attributes | ||||
| 
 | ||||
| ### show | ||||
| 
 | ||||
| A value indicates if the collapse body will be initialized visible or hidden. Should be one of the following values: | ||||
| 
 | ||||
| * `false` (default value) | ||||
| * `true` | ||||
| 
 | ||||
| ### multi | ||||
| 
 | ||||
| A value indicates if an `abp-collapse-body` can be shown or hidden by an element that can show/hide multiple collapse bodies. Basically, this attribute adds "multi-collapse" class to `abp-collapse-body`. Should be one of the following values: | ||||
| 
 | ||||
| * `false` (default value) | ||||
| * `true` | ||||
| 
 | ||||
| Sample: | ||||
| 
 | ||||
| ````xml | ||||
| <a abp-button="Primary" abp-collapse-id="FirstCollapseExample"> Toggle first element </a> | ||||
| <abp-button button-type="Primary" abp-collapse-id="SecondCollapseExample" text="Toggle second element" /> | ||||
| <abp-button button-type="Primary" abp-collapse-id="FirstCollapseExample SecondCollapseExample" text="Toggle both elements" /> | ||||
|          | ||||
| <abp-row class="mt-3"> | ||||
|     <abp-column size-sm="_6"> | ||||
|         <abp-collapse-body id="FirstCollapseExample" multi="true"> | ||||
|                Curabitur porta porttitor libero eu luctus. Praesent ultrices mattis commodo. Integer sodales massa risus, in molestie enim sagittis blandit | ||||
|         </abp-collapse-body> | ||||
|     </abp-column> | ||||
|     <abp-column size-sm="_6"> | ||||
|         <abp-collapse-body id="SecondCollapseExample" multi="true"> | ||||
|                 Anim pariatur  wolf moon tempor,,, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et.  | ||||
|         </abp-collapse-body> | ||||
|     </abp-column> | ||||
| </abp-row> | ||||
| ```` | ||||
| 
 | ||||
| ## Accordion example | ||||
| 
 | ||||
| `abp-accordion` is the main container for the accordion items.  | ||||
| 
 | ||||
| Basic usage: | ||||
| 
 | ||||
| ````xml | ||||
| <abp-accordion> | ||||
|     <abp-accordion-item title="Collapsible Group Item #1"> | ||||
|                 Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry rtat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. | ||||
|     </abp-accordion-item> | ||||
|     <abp-accordion-item title="Collapsible Group Item #2"> | ||||
|                 Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. | ||||
|     </abp-accordion-item> | ||||
|     <abp-accordion-item title="Collapsible Group Item #3"> | ||||
|                 Anim pariatur  wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. | ||||
|     </abp-accordion-item> | ||||
| </abp-accordion> | ||||
| ```` | ||||
| 
 | ||||
| ## Attributes | ||||
| 
 | ||||
| ### active | ||||
| 
 | ||||
| A value indicates if the accordion item will be initialized visible or hidden. Should be one of the following values: | ||||
| 
 | ||||
| * `false` (default value) | ||||
| * `true` | ||||
| 
 | ||||
| ### title | ||||
| 
 | ||||
| A value indicates the visible title of the accordion item. Should be a string value. | ||||
| @ -0,0 +1,97 @@ | ||||
| # Dropdowns | ||||
| 
 | ||||
| ## Introduction | ||||
| 
 | ||||
| `abp-dropdown` is the main container for dropdown content.  | ||||
| 
 | ||||
| Basic usage: | ||||
| 
 | ||||
| ````xml | ||||
| <abp-dropdown> | ||||
|     <abp-dropdown-button text="Dropdown button" /> | ||||
|     <abp-dropdown-menu> | ||||
|         <abp-dropdown-item href="#">Action</abp-dropdown-item> | ||||
|         <abp-dropdown-item href="#">Another action</abp-dropdown-item> | ||||
|         <abp-dropdown-item href="#">Something else here</abp-dropdown-item> | ||||
|     </abp-dropdown-menu> | ||||
| </abp-dropdown> | ||||
| ```` | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## Demo | ||||
| 
 | ||||
| See the [dropdown demo page](https://bootstrap-taghelpers.abp.io/Components/Dropdowns) to see it in action. | ||||
| 
 | ||||
| ## Attributes | ||||
| 
 | ||||
| ### direction | ||||
| 
 | ||||
| A value indicates which direction the dropdown buttons will be displayed to. Should be one of the following values: | ||||
| 
 | ||||
| * `Down` (default value) | ||||
| * `Up` | ||||
| * `Right` | ||||
| * `Left` | ||||
| 
 | ||||
| ### dropdown-style | ||||
| 
 | ||||
| A value indicates if an `abp-dropdown-button` will have split icon for dropdown. Should be one of the following values: | ||||
| 
 | ||||
| * `Single` (default value) | ||||
| * `Split` | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## Menu items | ||||
| 
 | ||||
| `abp-dropdown-menu` is the main container for dropdown menu items.  | ||||
| 
 | ||||
| Basic usage: | ||||
| 
 | ||||
| ````xml | ||||
| <abp-dropdown> | ||||
|     <abp-dropdown-button button-type="Secondary" text="Dropdown"/> | ||||
|     <abp-dropdown-menu> | ||||
|         <abp-dropdown-header>Dropdown Header</abp-dropdown-header> | ||||
|         <abp-dropdown-item href="#">Action</abp-dropdown-item> | ||||
|         <abp-dropdown-item active="true" href="#">Active action</abp-dropdown-item> | ||||
|         <abp-dropdown-item disabled="true" href="#">Disabled action</abp-dropdown-item> | ||||
|         <abp-dropdown-divider/> | ||||
|         <abp-dropdown-item-text>Dropdown Item Text</abp-dropdown-item-text> | ||||
|         <abp-dropdown-item href="#">Something else here</abp-dropdown-item> | ||||
|     </abp-dropdown-menu> | ||||
| </abp-dropdown> | ||||
| ```` | ||||
| 
 | ||||
| ## Attributes | ||||
| 
 | ||||
| ### align | ||||
| 
 | ||||
| A value indicates which direction `abp-dropdown-menu` items will be aligned to. Should be one of the following values: | ||||
| 
 | ||||
| * `Left` (default value) | ||||
| * `Right` | ||||
| 
 | ||||
| ### Additional content | ||||
| 
 | ||||
| `abp-dropdown-menu` can also contain additional HTML elements like headings, paragraphs, dividers or form element. | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| ````xml | ||||
| <abp-dropdown > | ||||
|     <abp-dropdown-button button-type="Secondary" text="Dropdown With Form"/> | ||||
|     <abp-dropdown-menu> | ||||
|         <form class="px-4 py-3"> | ||||
|             <abp-input asp-for="EmailAddress"></abp-input> | ||||
|             <abp-input asp-for="Password"></abp-input> | ||||
|             <abp-input asp-for="RememberMe"></abp-input> | ||||
|             <abp-button button-type="Primary" text="Sign In" type="submit" /> | ||||
|         </form> | ||||
|         <abp-dropdown-divider></abp-dropdown-divider> | ||||
|         <abp-dropdown-item href="#">New around here? Sign up</abp-dropdown-item> | ||||
|         <abp-dropdown-item href="#">Forgot password?</abp-dropdown-item> | ||||
|     </abp-dropdown-menu> | ||||
| </abp-dropdown> | ||||
| ```` | ||||
| @ -0,0 +1,78 @@ | ||||
| # List Groups | ||||
| 
 | ||||
| ## Introduction | ||||
| 
 | ||||
| `abp-list-group` is the main container for list group content.  | ||||
| 
 | ||||
| Basic usage: | ||||
| 
 | ||||
| ````xml | ||||
| <abp-list-group> | ||||
|     <abp-list-group-item>Cras justo odio</abp-list-group-item> | ||||
|     <abp-list-group-item>Dapibus ac facilisis in</abp-list-group-item> | ||||
|     <abp-list-group-item>Morbi leo risus</abp-list-group-item> | ||||
|     <abp-list-group-item>Vestibulum at eros</abp-list-group-item> | ||||
| </abp-list-group> | ||||
| ```` | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## Demo | ||||
| 
 | ||||
| See the [list groups demo page](https://bootstrap-taghelpers.abp.io/Components/ListGroups) to see it in action. | ||||
| 
 | ||||
| ## Attributes | ||||
| 
 | ||||
| ### flush | ||||
| 
 | ||||
| A value indicates `abp-list-group` items to remove some borders and rounded corners to render list group items edge-to-edge in a parent container. Should be one of the following values: | ||||
| 
 | ||||
| * `false` (default value) | ||||
| * `true` | ||||
| 
 | ||||
| ### active | ||||
| 
 | ||||
| A value indicates if an `abp-list-group-item` to be active. Should be one of the following values: | ||||
| 
 | ||||
| * `false` (default value) | ||||
| * `true` | ||||
| 
 | ||||
| ### disabled | ||||
| 
 | ||||
| A value indicates if an `abp-list-group-item` to be disabled. Should be one of the following values: | ||||
| 
 | ||||
| * `false` (default value) | ||||
| * `true` | ||||
| 
 | ||||
| ### href | ||||
| 
 | ||||
| A value indicates if an `abp-list-group-item` has a link. Should be a string link value.  | ||||
| 
 | ||||
| ### type | ||||
| 
 | ||||
| A value indicates an `abp-list-group-item` style class with a stateful background and color. Should be one of the following values: | ||||
| 
 | ||||
| * `Default` (default value) | ||||
| * `Primary` | ||||
| * `Secondary` | ||||
| * `Success` | ||||
| * `Danger` | ||||
| * `Warning` | ||||
| * `Info` | ||||
| * `Light` | ||||
| * `Dark` | ||||
| * `Link` | ||||
| 
 | ||||
| ### Additional content | ||||
| 
 | ||||
| `abp-list-group-item` can also contain additional HTML elements like spans. | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| ````xml | ||||
| <abp-list-group> | ||||
|     <abp-list-group-item>Cras justo odio <span abp-badge-pill="Primary">14</span></abp-list-group-item> | ||||
|     <abp-list-group-item>Dapibus ac facilisis in <span abp-badge-pill="Primary">2</span></abp-list-group-item> | ||||
|     <abp-list-group-item>Morbi leo risus <span abp-badge-pill="Primary">1</span></abp-list-group-item> | ||||
| </abp-list-group> | ||||
| ```` | ||||
| @ -0,0 +1,81 @@ | ||||
| # Modals | ||||
| 
 | ||||
| ## Introduction | ||||
| 
 | ||||
| `abp-modal` is a main element to create a modal. | ||||
| 
 | ||||
| Basic usage: | ||||
| 
 | ||||
| ````xml | ||||
| <abp-button button-type="Primary" data-toggle="modal" data-target="#myModal">Launch modal</abp-button> | ||||
| 
 | ||||
| <abp-modal centered="true" size="Large" id="myModal"> | ||||
|    <abp-modal-header title="Modal title"></abp-modal-header> | ||||
|    <abp-modal-body> | ||||
|        Woohoo, you're reading this text in a modal! | ||||
|    </abp-modal-body> | ||||
|    <abp-modal-footer buttons="Close"></abp-modal-footer> | ||||
| </abp-modal> | ||||
| ```` | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## Demo | ||||
| 
 | ||||
| See the [modals demo page](https://bootstrap-taghelpers.abp.io/Components/Modals) to see it in action. | ||||
| 
 | ||||
| ## Attributes | ||||
| 
 | ||||
| ### centered | ||||
| 
 | ||||
| A value indicates the positioning of the modal. Should be one of the following values: | ||||
| 
 | ||||
| * `false` (default value) | ||||
| * `true` | ||||
| 
 | ||||
| ### size | ||||
| 
 | ||||
| A value indicates the size of the modal. Should be one of the following values: | ||||
| 
 | ||||
| * `Default` (default value) | ||||
| * `Small` | ||||
| * `Large` | ||||
| * `ExtraLarge` | ||||
| 
 | ||||
| ### static | ||||
| 
 | ||||
| A value indicates if the modal will be static. Should be one of the following values: | ||||
| 
 | ||||
| * `false` (default value) | ||||
| * `true` | ||||
| 
 | ||||
| ### Additional content | ||||
| 
 | ||||
| `abp-modal-footer` can have multiple buttons with alignment option. | ||||
| 
 | ||||
| Add `@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal` to your page. | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| ````xml | ||||
| <abp-button button-type="Primary" data-toggle="modal" data-target="#myModal">Launch modal</abp-button> | ||||
| 
 | ||||
| <abp-modal centered="true" size="Large" id="myModal" static="true"> | ||||
|     <abp-modal-header title="Modal title"></abp-modal-header> | ||||
|     <abp-modal-body> | ||||
|         Woohoo, you're reading this text in a modal! | ||||
|     </abp-modal-body> | ||||
|     <abp-modal-footer buttons="@(AbpModalButtons.Save|AbpModalButtons.Close)" button-alignment="Between"></abp-modal-footer> | ||||
| </abp-modal> | ||||
| ```` | ||||
| 
 | ||||
| ### button-alignment | ||||
| 
 | ||||
| A value indicates the positioning of your modal footer buttons. Should be one of the following values:  | ||||
| 
 | ||||
| * `Default` (default value) | ||||
| * `Start` | ||||
| * `Center` | ||||
| * `Around` | ||||
| * `Between` | ||||
| * `End` | ||||
| @ -0,0 +1,57 @@ | ||||
| # Paginator | ||||
| 
 | ||||
| ## Introduction | ||||
| 
 | ||||
| `abp-paginator` is the abp tag for pagination. Requires `Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination.PagerModel` type of model. | ||||
| 
 | ||||
| Basic usage: | ||||
| 
 | ||||
| ````xml | ||||
| <abp-paginator model="Model.PagerModel" show-info="true"></abp-paginator> | ||||
| ```` | ||||
| 
 | ||||
| Model: | ||||
| 
 | ||||
| ````xml | ||||
| using Microsoft.AspNetCore.Mvc.RazorPages; | ||||
| using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination; | ||||
| 
 | ||||
| namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components | ||||
| { | ||||
|     public class PaginatorModel : PageModel | ||||
|     { | ||||
|         public PagerModel PagerModel { get; set; } | ||||
| 
 | ||||
|         public void OnGet(int currentPage, string sort) | ||||
|         { | ||||
|             PagerModel = new PagerModel(100, 10, currentPage, 10, "Paginator", sort); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| ```` | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## Demo | ||||
| 
 | ||||
| See the [paginator demo page](https://bootstrap-taghelpers.abp.io/Components/Paginator) to see it in action. | ||||
| 
 | ||||
| ## Attributes | ||||
| 
 | ||||
| ### model | ||||
| 
 | ||||
| `Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination.PagerModel` type of model can be initialized with the following data: | ||||
| 
 | ||||
| * `totalCount` | ||||
| * `shownItemsCount` | ||||
| * `currentPage` | ||||
| * `pageSize` | ||||
| * `pageUrl` | ||||
| * `sort` (default null) | ||||
| 
 | ||||
| ### show-info | ||||
| 
 | ||||
| A value indicates if an extra information about start, end and total records will be displayed. Should be one of the following values: | ||||
| 
 | ||||
| * `false` (default value) | ||||
| * `true` | ||||
| @ -0,0 +1,70 @@ | ||||
| # Progress Bars | ||||
| 
 | ||||
| ## Introduction | ||||
| 
 | ||||
| `abp-progress-bar` is the abp tag for progress bar status. | ||||
| 
 | ||||
| Basic usage: | ||||
| 
 | ||||
| ````xml | ||||
| <abp-progress-bar value="70" /> | ||||
| 
 | ||||
| <abp-progress-bar type="Warning" value="25"> %25 </abp-progress-bar> | ||||
| 
 | ||||
| <abp-progress-bar type="Success" value="40" strip="true"/> | ||||
| 
 | ||||
| <abp-progress-bar type="Dark" value="10" min-value="5" max-value="15" strip="true"> %50 </abp-progress-bar> | ||||
| 
 | ||||
| <abp-progress-group> | ||||
|     <abp-progress-part type="Success" value="25"/> | ||||
|     <abp-progress-part type="Danger" value="10" strip="true"> %10 </abp-progress-part> | ||||
|     <abp-progress-part type="Primary" value="50" animation="true" strip="true" /> | ||||
| </abp-progress-group> | ||||
| ```` | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## Demo | ||||
| 
 | ||||
| See the [progress bars demo page](https://bootstrap-taghelpers.abp.io/Components/Progress-Bars) to see it in action. | ||||
| 
 | ||||
| ## Attributes | ||||
| 
 | ||||
| ### value | ||||
| 
 | ||||
| A value indicates the current progress of the bar. | ||||
| 
 | ||||
| ### type | ||||
| 
 | ||||
| A value indicates the background color of the progress bar. Should be one of the following values: | ||||
| 
 | ||||
| * `Default` (default value) | ||||
| * `Secondary` | ||||
| * `Success` | ||||
| * `Danger` | ||||
| * `Warning` | ||||
| * `Info` | ||||
| * `Light` | ||||
| * `Dark` | ||||
| 
 | ||||
| ### min-value | ||||
| 
 | ||||
| Minimum value of the progress bar. Default is 0. | ||||
| 
 | ||||
| ### max-value | ||||
| 
 | ||||
| Maximum value of the progress bar. Default is 100. | ||||
| 
 | ||||
| ### strip | ||||
| 
 | ||||
| A value indicates if the background style of the progress bar is stripped. Should be one of the following values: | ||||
| 
 | ||||
| * `false` (default value) | ||||
| * `true` | ||||
| 
 | ||||
| ### animation | ||||
| 
 | ||||
| A value indicates if the stripped background style of the progress bar is animated. Should be one of the following values: | ||||
| 
 | ||||
| * `false` (default value) | ||||
| * `true` | ||||
| @ -0,0 +1,35 @@ | ||||
| # Tooltips | ||||
| 
 | ||||
| ## Introduction | ||||
| 
 | ||||
| `abp-tooltip` is the abp tag for tooltips. | ||||
| 
 | ||||
| Basic usage: | ||||
| 
 | ||||
| ````xml | ||||
| <abp-button abp-tooltip="Tooltip"> | ||||
|       Tooltip Default | ||||
| </abp-button> | ||||
| 
 | ||||
| <abp-button abp-tooltip-top="Tooltip"> | ||||
|       Tooltip on top | ||||
| </abp-button> | ||||
| 
 | ||||
| <abp-button abp-tooltip-right="Tooltip"> | ||||
|       Tooltip on right | ||||
| </abp-button> | ||||
| 
 | ||||
| <abp-button abp-tooltip-bottom="Tooltip"> | ||||
|       Tooltip on bottom | ||||
| </abp-button> | ||||
| 
 | ||||
| <abp-button disabled="true" abp-tooltip="Tooltip"> | ||||
|       Disabled button Tooltip | ||||
| </abp-button> | ||||
| ```` | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## Demo | ||||
| 
 | ||||
| See the [tooltips demo page](https://bootstrap-taghelpers.abp.io/Components/Tooltips) to see it in action. | ||||
| @ -1,3 +1,166 @@ | ||||
| # 自定义应用模块: 覆盖服务 | ||||
| # 自定义应用模块: 重写服务 | ||||
| 
 | ||||
| TODO... | ||||
| 你可能想要**更改**依赖模块的**行为(业务逻辑)**. 在这种情况下,你可以使用[依赖注入](Dependency-Injection.md)的能力替换服务,控制器甚至页面模型到你自己的实现. | ||||
| 
 | ||||
| 注册到依赖注入的任何类,包括ABP框架的服务都可以被**替换**. | ||||
| 
 | ||||
| 你可以根据自己的需求使用不同的选项,下面的章节中将介绍这些选项. | ||||
| 
 | ||||
| > 请注意,某些服务方法可能不是virtual,你可能无法override,我们会通过设计将其virtual,如果你发现任何方法不可以被覆盖,请[创建一个issue](https://github.com/abpframework/abp/issues/new)或者你直接修改后并发送**pull request**到GitHub. | ||||
| 
 | ||||
| ## 替换接口 | ||||
| 
 | ||||
| 如果给定的服务定义了接口,像 `IdentityUserAppService` 类实现了 `IIdentityUserAppService` 接口,你可以为这个接口创建自己的实现并且替换当前的实现. 例如: | ||||
| 
 | ||||
| ````csharp | ||||
| public class MyIdentityUserAppService : IIdentityUserAppService, ITransientDependency | ||||
| { | ||||
|     //... | ||||
| } | ||||
| ```` | ||||
| 
 | ||||
| `MyIdentityUserAppService` 通过命名约定替换了 `IIdentityUserAppService` 的当前实现. 如果你的类名不匹配,你需要手动公开服务接口: | ||||
| 
 | ||||
| ````csharp | ||||
| [ExposeServices(typeof(IIdentityUserAppService))] | ||||
| public class TestAppService : IIdentityUserAppService, ITransientDependency | ||||
| { | ||||
|     //... | ||||
| } | ||||
| ```` | ||||
| 
 | ||||
| 依赖注入系统允许为一个接口注册多个服务. 注入接口时会解析最后一个注入的服务. 显式的替换服务是一个好习惯. | ||||
| 
 | ||||
| 示例: | ||||
| 
 | ||||
| ````csharp | ||||
| [Dependency(ReplaceServices = true)] | ||||
| [ExposeServices(typeof(IIdentityUserAppService))] | ||||
| public class TestAppService : IIdentityUserAppService, ITransientDependency | ||||
| { | ||||
|     //... | ||||
| } | ||||
| ```` | ||||
| 
 | ||||
| 使用这种方法, `IIdentityUserAppService` 接口将只会有一个实现. 也可以使用以下方法替换服务: | ||||
| 
 | ||||
| ````csharp | ||||
| context.Services.Replace( | ||||
|     ServiceDescriptor.Transient<IIdentityUserAppService, MyIdentityUserAppService>() | ||||
| ); | ||||
| ```` | ||||
| 
 | ||||
| 你可以在[模块](Module-Development-Basics.md)类的 `ConfigureServices` 方法编写替换服务代码. | ||||
| 
 | ||||
| ## 重写一个服务类 | ||||
| 
 | ||||
| 大多数情况下,你会仅想改变服务当前实现的一个或几个方法. 重新实现完整的接口变的繁琐,更好的方法是继承原始类并重写方法。 | ||||
| 
 | ||||
| ### 示例: 重写服务方法 | ||||
| 
 | ||||
| ````csharp | ||||
| [Dependency(ReplaceServices = true)] | ||||
| public class MyIdentityUserAppService : IdentityUserAppService | ||||
| { | ||||
|     //... | ||||
|     public MyIdentityUserAppService( | ||||
|         IdentityUserManager userManager, | ||||
|         IIdentityUserRepository userRepository, | ||||
|         IGuidGenerator guidGenerator | ||||
|     ) : base( | ||||
|         userManager, | ||||
|         userRepository, | ||||
|         guidGenerator) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     public override async Task<IdentityUserDto> CreateAsync(IdentityUserCreateDto input) | ||||
|     { | ||||
|         if (input.PhoneNumber.IsNullOrWhiteSpace()) | ||||
|         { | ||||
|             throw new AbpValidationException( | ||||
|                 "Phone number is required for new users!", | ||||
|                 new List<ValidationResult> | ||||
|                 { | ||||
|                     new ValidationResult( | ||||
|                         "Phone number can not be empty!", | ||||
|                         new []{"PhoneNumber"} | ||||
|                     ) | ||||
|                 } | ||||
|             );        } | ||||
| 
 | ||||
|         return await base.CreateAsync(input); | ||||
|     } | ||||
| } | ||||
| ```` | ||||
| 
 | ||||
| 示例中**重写**了 `IdentityUserAppService` [应用程序](Application-Services.md) `CreateAsync` 方法检查手机号码. 然后调用了基类方法继续**基本业务逻辑**. 通过这种方法你可以在基本业务逻辑**之前**和**之后**执行其他业务逻辑. | ||||
| 
 | ||||
| 你也可以完全**重写**整个业务逻辑去创建用户,而不是调用基类方法. | ||||
| 
 | ||||
| ### 示例: 重写领域服务 | ||||
| 
 | ||||
| ````csharp | ||||
| [Dependency(ReplaceServices = true)] | ||||
| [ExposeServices(typeof(IdentityUserManager))] | ||||
| public class MyIdentityUserManager : IdentityUserManager | ||||
| { | ||||
|     public MyIdentityUserManager( | ||||
|         IdentityUserStore store,  | ||||
|         IOptions<IdentityOptions> optionsAccessor,  | ||||
|         IPasswordHasher<IdentityUser> passwordHasher, | ||||
|         IEnumerable<IUserValidator<IdentityUser>> userValidators,  | ||||
|         IEnumerable<IPasswordValidator<IdentityUser>> passwordValidators,  | ||||
|         ILookupNormalizer keyNormalizer,  | ||||
|         IdentityErrorDescriber errors,  | ||||
|         IServiceProvider services,  | ||||
|         ILogger<IdentityUserManager> logger,  | ||||
|         ICancellationTokenProvider cancellationTokenProvider | ||||
|         ) : base( | ||||
|             store,  | ||||
|             optionsAccessor,  | ||||
|             passwordHasher,  | ||||
|             userValidators,  | ||||
|             passwordValidators,  | ||||
|             keyNormalizer,  | ||||
|             errors,  | ||||
|             services,  | ||||
|             logger,  | ||||
|             cancellationTokenProvider) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     public override async Task<IdentityResult> CreateAsync(IdentityUser user) | ||||
|     { | ||||
|         if (user.PhoneNumber.IsNullOrWhiteSpace()) | ||||
|         { | ||||
|             throw new AbpValidationException( | ||||
|                 "Phone number is required for new users!", | ||||
|                 new List<ValidationResult> | ||||
|                 { | ||||
|                     new ValidationResult( | ||||
|                         "Phone number can not be empty!", | ||||
|                         new []{"PhoneNumber"} | ||||
|                     ) | ||||
|                 } | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         return await base.CreateAsync(user); | ||||
|     } | ||||
| } | ||||
| ```` | ||||
| 
 | ||||
| 示例中类继承了 `IdentityUserManager` [领域服务](Domain-Services.md),并且重写了 `CreateAsync` 方法进行了与之前相同的手机号码检查. 结果也是一样的,但是这次我们在领域服务实现了它,假设这是我们系统的**核心领域逻辑**. | ||||
| 
 | ||||
| > 这里需要 `[ExposeServices(typeof(IdentityUserManager))]`  attribute,因为 `IdentityUserManager` 没有定义接口 (像 `IIdentityUserManager`) ,依赖注入系统并不会按照约定公开继承类的服务(如已实现的接口). | ||||
| 
 | ||||
| 参阅[本地化系统](Localization.md)了解如何自定义错误消息. | ||||
| 
 | ||||
| ### 重写其他服务 | ||||
| 
 | ||||
| 控制器,框架服务,视图组件类以及其他类型注册到依赖注入的类都可以像上面的示例那样被重写. | ||||
| 
 | ||||
| ## 如何找到服务? | ||||
| 
 | ||||
| [模块文档](Modules/Index.md) 包含了定义的主要服务列表. 另外 你也可以查看[源码](https://github.com/abpframework/abp/tree/dev/modules)找到所有的服务. | ||||
| @ -1,18 +1,17 @@ | ||||
| import { Injectable, TrackByFunction } from '@angular/core'; | ||||
| import { O } from 'ts-toolbelt'; | ||||
| 
 | ||||
| export const trackBy = <T = any>(key: keyof T): TrackByFunction<T> => (_, item) => item[key]; | ||||
| 
 | ||||
| export const trackByDeep = <T = any>( | ||||
|   ...keys: T extends object ? O.Paths<T> : never | ||||
| ): TrackByFunction<T> => (_, item) => keys.reduce((acc, key) => acc[key], item); | ||||
| 
 | ||||
| @Injectable({ | ||||
|   providedIn: 'root', | ||||
| }) | ||||
| export class TrackByService<ItemType = any> { | ||||
|   by<T = ItemType>(key: keyof T): TrackByFunction<T> { | ||||
|     return ({}, item) => item[key]; | ||||
|   } | ||||
| 
 | ||||
|   byDeep<T = ItemType>(...keys: (string | number)[]): TrackByFunction<T> { | ||||
|     return ({}, item) => keys.reduce((acc, key) => acc[key], item); | ||||
|   } | ||||
|   by = trackBy; | ||||
| 
 | ||||
|   bySelf<T = ItemType>(): TrackByFunction<T> { | ||||
|     return ({}, item) => item; | ||||
|   } | ||||
|   byDeep = trackByDeep; | ||||
| } | ||||
|  | ||||
					Loading…
					
					
				
		Reference in new issue
	
	 Armağan Ünlü
						Armağan Ünlü