@ -0,0 +1,143 @@ |
|||||
|
# Entity Filters |
||||
|
|
||||
|
Every CRUD page includes some sort of inputs to filter the listed data. Some of the inputs are common among all of the entities like the `Search` box. In addition, every entity has its own advanced filters depending on its fields. To reduce the amount of code written on every CRUD page, the Angular UI of ABP Commercial introduces a new type of component called `abp-advanced-entity-filters` |
||||
|
|
||||
|
## Setup |
||||
|
|
||||
|
The components are in the _@volo/abp.commercial.ng.ui_ package, which is included in the ABP templates. So, as long as your project is a product of these templates and unless you delete the package, you have access to the entity filter components. |
||||
|
You can either import the `CommercialUiModule` which contains other components as well as `AdvancedEntityFilters` or you can directly import the `AdvancedEntityFiltersModule` if you do not need other components. Here is how you import them in your Angular module: |
||||
|
|
||||
|
```javascript |
||||
|
import { |
||||
|
CommercialUiModule, |
||||
|
AdvancedEntityFiltersModule, |
||||
|
} from "@volo/abp.commercial.ng.ui"; |
||||
|
|
||||
|
@NgModule({ |
||||
|
imports: [ |
||||
|
// other imports |
||||
|
CommercialUiModule, |
||||
|
|
||||
|
// OR |
||||
|
|
||||
|
AdvancedEntityFiltersModule, |
||||
|
], |
||||
|
// rest of the module metadata |
||||
|
}) |
||||
|
export class YourModule {} |
||||
|
``` |
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
Let's take a look at the `Users` page from the `Identity` module. |
||||
|
|
||||
|
 |
||||
|
|
||||
|
As shown in the screenshot, `abp-advanced-entity-filters` usually contain two parts, an entity filter (common among entities), i.e. `abp-entity-filter`, and entity-specific filters which are encapsulated within the `abp-advanced-entity-filters-form` component. |
||||
|
|
||||
|
`users.component.html` |
||||
|
|
||||
|
```html |
||||
|
<abp-advanced-entity-filters [list]="list" localizationSourceName="AbpIdentity"> |
||||
|
<abp-advanced-entity-filters-form> |
||||
|
<form #filterForm (keyup.enter)="list.get()"> |
||||
|
<div class="row"> |
||||
|
<!-- Form elements are omitted --> |
||||
|
|
||||
|
<div class="col-12 col-sm-auto align-self-end mb-3"> |
||||
|
<div class="row"> |
||||
|
<div class="col-6 col-sm-auto d-grid"> |
||||
|
<button |
||||
|
type="button" |
||||
|
class="btn btn-outline-primary" |
||||
|
(click)="clearFilters()" |
||||
|
> |
||||
|
<span>{%{{{ 'AbpUi::Clear' | abpLocalization }}}%}</span> |
||||
|
</button> |
||||
|
</div> |
||||
|
<div class="col-6 col-sm-auto d-grid"> |
||||
|
<button |
||||
|
type="button" |
||||
|
class="btn btn-primary" |
||||
|
(click)="list.get()" |
||||
|
> |
||||
|
<span>{%{{{ 'AbpUi::Refresh' | abpLocalization }}}%}</span> |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</form> |
||||
|
</abp-advanced-entity-filters-form> |
||||
|
</abp-advanced-entity-filters> |
||||
|
``` |
||||
|
|
||||
|
The `abp-advanced-entity-filters` already contains the `abp-entity-filter` component so you do not need to pass it. However, the `abp-entity-filter` component needs an instance of `ListService` which is usually stored in the `list` field of the page. You can also change the placeholder of the component via `entityFilterPlaceholder` input which is passed into the `abpLocalization` pipe so that it uses the translated text. Default is `'AbpUi::PagerSearch'` |
||||
|
|
||||
|
E.g |
||||
|
|
||||
|
```html |
||||
|
<abp-advanced-entity-filters |
||||
|
[list]="list" |
||||
|
entityFilterPlaceholder="AbpUi::PagerSearch" |
||||
|
> |
||||
|
<!-- ... --> |
||||
|
</abp-advanced-entity-filters> |
||||
|
``` |
||||
|
|
||||
|
### Inputs |
||||
|
|
||||
|
- `list`: an instance of `ListService` |
||||
|
- `entityFilterPlaceholder`: the placeholder of `abp-entity-filter` component. Default: `'AbpUi::PagerSearch'` |
||||
|
- `localizationSourceName`: the localization source of the current page. E.g: `AbpIdentity` |
||||
|
|
||||
|
### Inner components |
||||
|
|
||||
|
Some entities are simple and do not require any filter other than the `abp-entity-filter`. In this case, you can simply use the `abp-advanced-entity-filters` without anything in between. |
||||
|
|
||||
|
E.g. |
||||
|
|
||||
|
Let's remove `form` from the `Users` page |
||||
|
|
||||
|
```html |
||||
|
<abp-advanced-entity-filters [list]="list" localizationSourceName="AbpIdentity"> |
||||
|
</abp-advanced-entity-filters> |
||||
|
``` |
||||
|
|
||||
|
 |
||||
|
|
||||
|
If your component needs other filters, you can pass your own `form` within the `abp-advanced-entity-filters-form` component. This will render your form as well as a toggle (`abp-advanced-entity-filters-toggle`) to show and hide the form |
||||
|
|
||||
|
E.g. |
||||
|
|
||||
|
```html |
||||
|
<abp-advanced-entity-filters [list]="list" localizationSourceName="AbpIdentity"> |
||||
|
<abp-advanced-entity-filters-form> |
||||
|
<form> |
||||
|
<!-- Content is omitted for sake of simplicity --> |
||||
|
</form> |
||||
|
</abp-advanced-entity-filters-form> |
||||
|
</abp-advanced-entity-filters> |
||||
|
``` |
||||
|
|
||||
|
 |
||||
|
|
||||
|
Last but not least, if you need to render some content above the `abp-entity-filter` component, you can use the `abp-advanced-entity-filters-above-search`. |
||||
|
|
||||
|
E.g. |
||||
|
|
||||
|
```html |
||||
|
<abp-advanced-entity-filters [list]="list" localizationSourceName="AbpIdentity"> |
||||
|
<abp-advanced-entity-filters-above-search> |
||||
|
<h3>Custom Content above entity-filter</h3> |
||||
|
</abp-advanced-entity-filters-above-search> |
||||
|
|
||||
|
<abp-advanced-entity-filters-form> |
||||
|
<form> |
||||
|
<!-- Content is omitted for sake of simplicity --> |
||||
|
</form> |
||||
|
</abp-advanced-entity-filters-form> |
||||
|
</abp-advanced-entity-filters> |
||||
|
``` |
||||
|
|
||||
|
 |
||||
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 224 KiB |
|
After Width: | Height: | Size: 228 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 38 KiB |
@ -0,0 +1,98 @@ |
|||||
|
# Lookup Components |
||||
|
|
||||
|
The Angular UI of ABP Commercial introduces some components with `abp-lookup-...` selector prefix. These components are used for retrieving relational entity data. |
||||
|
|
||||
|
## Setup |
||||
|
|
||||
|
The components are in the _@volo/abp.commercial.ng.ui_ package, which is included in the ABP templates. So, as long as your project is a product of these templates and unless you delete the package, you have access to the lookup components. Here is how you import them in your Angular module: |
||||
|
|
||||
|
```javascript |
||||
|
import { CommercialUiModule } from '@volo/abp.commercial.ng.ui'; |
||||
|
|
||||
|
@NgModule({ |
||||
|
imports: [ |
||||
|
// other imports |
||||
|
CommercialUiModule, |
||||
|
], |
||||
|
// rest of the module metadata |
||||
|
}) |
||||
|
export class YourModule {} |
||||
|
``` |
||||
|
|
||||
|
Now you can use the lookup components in your components declared by this module. |
||||
|
|
||||
|
## Lookup HTTP Requests |
||||
|
|
||||
|
The lookup requests are used by all lookup components to get the related entity records. Because of lexical this, _they must be arrow functions_. |
||||
|
|
||||
|
```javascript |
||||
|
@Injectable({ |
||||
|
providedIn: 'root' |
||||
|
}) |
||||
|
export class AuthorService { |
||||
|
getCountryLookup = (input: LookupRequestDto) => |
||||
|
this.restService.request<any, PagedResultDto<LookupDto<string>>>({ |
||||
|
method: 'GET', |
||||
|
url: '/api/app/authors/country-lookup', |
||||
|
params: { filter: input.filter, skipCount: input.skipCount, maxResultCount: input.maxResultCount }, |
||||
|
}, |
||||
|
{ apiName: this.apiName }); |
||||
|
|
||||
|
// rest of the service is removed for brevity |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Lookup Typeahead Component |
||||
|
|
||||
|
Typeahead is a good choice when you have an unknown number of records for the related entity or you want to improve the UX with a search ability. Although not the best scenario, the country picker below shows how the lookup typeahead works: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
Here is how it is used in the template. |
||||
|
|
||||
|
```html |
||||
|
<abp-lookup-typeahead |
||||
|
cid="author-country-id" |
||||
|
formControlName="countryId" |
||||
|
displayNameProp="name" |
||||
|
[editingData]="selected?.country" |
||||
|
[getFn]="service.getCountryLookup" |
||||
|
></abp-lookup-typeahead> |
||||
|
``` |
||||
|
|
||||
|
The available properties are as follows: |
||||
|
|
||||
|
- **cid:** The id of the form control (e.g. an input or a select element) inside the lookup component. Lets form controls respond to `<label>` events. |
||||
|
- **editingData:** The related entity data if a record is being updated. |
||||
|
- **displayNameProp:** The property of the updated record to use as a display name in the form control. |
||||
|
- **lookupNameProp:** The property of the entity to use as a display name in options. Should macth the lookup HTTP request interface. _(default: displayName)_ |
||||
|
- **lookupIdProp:** The property of the entity to use as the unique key in options. Should macth the lookup HTTP request interface. _(default: id)_ |
||||
|
- **maxResultCount:** The maximum number of options to display. _(default: 10)_ |
||||
|
- **getFn:** A function to get the related entity records with HTTP requests. Because of lexical this, _it must be a an arrow function_. |
||||
|
- **disabled:** This property lets you disable/enable a lookup component. _(default: false)_. |
||||
|
|
||||
|
## Lookup Select Component |
||||
|
|
||||
|
Select is a good choice when you have a low (and usually fixed) number of records for the related entity and search is not necessary. The country picker below shows how the lookup select works: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
Here is how it is used in the template. |
||||
|
|
||||
|
```html |
||||
|
<abp-lookup-select |
||||
|
cid="author-country-id" |
||||
|
formControlName="countryId" |
||||
|
displayNameProp="name" |
||||
|
[getFn]="service.getCountryLookup" |
||||
|
></abp-lookup-select> |
||||
|
``` |
||||
|
|
||||
|
The available properties are as follows: |
||||
|
|
||||
|
- **cid:** The id of the form control (e.g. an input or a select element) inside the lookup component. Lets form controls respond to `<label>` events. |
||||
|
- **displayNameProp:** The property of the updated record to use as a display name in the form control. |
||||
|
- **lookupNameProp:** The property of the entity to use as a display name in options. Should macth the lookup HTTP request interface. _(default: displayName)_ |
||||
|
- **lookupIdProp:** The property of the entity to use as the unique key in options. Should macth the lookup HTTP request interface. _(default: id)_ |
||||
|
- **getFn:** A function to get the related entity records with HTTP requests. Because of lexical this, _it must be a an arrow function_. |
||||
|
- **disabled:** This property lets you disable/enable a lookup component. _(default: false)_. |
||||
@ -0,0 +1,77 @@ |
|||||
|
# Manage Profile Page Tabs |
||||
|
|
||||
|
 |
||||
|
|
||||
|
The tabs in the manage profile page can be managed via `ManageProfileTabsService` which is exposed by the `@volo/abp.ng.account/public/config` package. You can add, remove, or edit a tab with using this service. |
||||
|
|
||||
|
See the example below, covers all features: |
||||
|
|
||||
|
```ts |
||||
|
// manage-profile-tabs.provider.ts |
||||
|
|
||||
|
import { APP_INITIALIZER, Component } from "@angular/core"; |
||||
|
import { TwoFactorTabComponent } from "@volo/abp.ng.account/public"; |
||||
|
import { |
||||
|
eAccountManageProfileTabNames, |
||||
|
ManageProfileTabsService, |
||||
|
} from "@volo/abp.ng.account/public/config"; |
||||
|
import { MyAwesomeTabComponent } from "./my-awesome-tab/my-awesome-tab.component"; |
||||
|
|
||||
|
@Component({ |
||||
|
standalone: true, |
||||
|
selector: "abp-my-awesome-tab", |
||||
|
template: `My Awesome Tab`, |
||||
|
}) |
||||
|
class MyAwesomeTabComponent {} |
||||
|
|
||||
|
export const MANAGE_PROFILE_TAB_PROVIDER = { |
||||
|
provide: APP_INITIALIZER, |
||||
|
useFactory: configureManageProfileTabs, |
||||
|
deps: [ManageProfileTabsService], |
||||
|
multi: true, |
||||
|
}; |
||||
|
|
||||
|
export function configureManageProfileTabs(tabs: ManageProfileTabsService) { |
||||
|
return () => { |
||||
|
tabs.add([ |
||||
|
{ |
||||
|
name: "::MyAwesomeTab", // supports localization keys |
||||
|
order: 5, |
||||
|
component: MyAwesomeTabComponent, |
||||
|
}, |
||||
|
]); |
||||
|
|
||||
|
tabs.patch(eAccountManageProfileTabNames.TwoFactor, { |
||||
|
name: "Two factor authentication", |
||||
|
component: TwoFactorTabComponent, |
||||
|
}); |
||||
|
|
||||
|
tabs.remove([eAccountManageProfileTabNames.ProfilePicture]); |
||||
|
}; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
```ts |
||||
|
//app.module.ts |
||||
|
|
||||
|
import { MANAGE_PROFILE_TAB_PROVIDER } from "./manage-profile-tabs.provider"; |
||||
|
|
||||
|
@NgModule({ |
||||
|
providers: [MANAGE_PROFILE_TAB_PROVIDER], |
||||
|
}) |
||||
|
export class AppModule {} |
||||
|
``` |
||||
|
|
||||
|
What we have done above; |
||||
|
|
||||
|
- Created the `manage-profile-page-tabs.provider.ts`. |
||||
|
- Determined the `configureManageProfileTabs` function to perform manage profile tabs actions. |
||||
|
- Added a new tab labeled "My awesome tab". |
||||
|
- Renamed the "Two factor" tab label. |
||||
|
- Removed the "Profile picture" tab. |
||||
|
- Determined the `MANAGE_PROFILE_TAB_PROVIDER` to be able to run the `configureManageProfileTabs` function on initialization. |
||||
|
- Registered the `MANAGE_PROFILE_TAB_PROVIDER` to the `AppModule` providers. |
||||
|
|
||||
|
See the result: |
||||
|
|
||||
|
 |
||||
@ -0,0 +1,14 @@ |
|||||
|
# Angular UI |
||||
|
|
||||
|
[Angular](https://angular.dev/) is a framework for building interactive, client-side web UIs using [TypeScript](https://www.typescriptlang.org) and [NodeJS](https://nodejs.org). |
||||
|
|
||||
|
ABP Angular provides the infrastructure to communicate with the ABP backend and offers utilities to simplify frontend development. We’ll explore and dive into the details under the following main topics: |
||||
|
|
||||
|
- Development |
||||
|
- Core Functionality |
||||
|
- Utilities |
||||
|
- Customization |
||||
|
- Components |
||||
|
|
||||
|
|
||||
|
You can also make a [Quick Start](./quick-start.md) |
||||