Browse Source

Merge pull request #7036 from abpframework/auto-merge/rel-4-1/44

Merge branch dev with rel-4.1
pull/7038/head
Mehmet Erim 6 years ago
committed by GitHub
parent
commit
52857084fc
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 85
      docs/en/UI/Angular/Data-Table-Column-Extensions.md
  2. 74
      docs/en/UI/Angular/Dynamic-Form-Extensions.md
  3. 320
      docs/en/UI/Angular/Entity-Action-Extensions.md
  4. 126
      docs/en/UI/Angular/Page-Toolbar-Extensions.md
  5. BIN
      docs/en/UI/Angular/images/entity-action-extensions---click-me.gif
  6. BIN
      docs/en/UI/Angular/images/entity-action-extensions---custom-modal.gif
  7. BIN
      docs/en/UI/Angular/images/entity-prop-extensions---custom-cell.gif
  8. BIN
      docs/en/UI/Angular/images/entity-prop-extensions---name-column.gif
  9. BIN
      docs/en/UI/Angular/images/form-prop-extensions---birthday-field.gif
  10. BIN
      docs/en/UI/Angular/images/toolbar-action-extensions---click-me.gif
  11. BIN
      docs/en/UI/Angular/images/toolbar-action-extensions---custom-component.gif

85
docs/en/UI/Angular/Data-Table-Column-Extensions.md

@ -5,7 +5,7 @@
Entity prop extension system allows you to add a new column to the data table for an entity or change/remove an already existing one. A "Name" column was added to the user management page below:
![Entity Prop Extension Example: "Name" Column](images/user-prop-extension-name-column-ng.png)
<img alt="Entity Prop Extension Example: 'Name' Column" src="./images/entity-prop-extensions---name-column.gif" width="800px" style="max-width:100%">
You will have access to the current entity in your code and display its value, make the column sortable, perform visibility checks, and more. You can also render custom HTML in table cells.
@ -18,10 +18,14 @@ In this example, we will add a "Name" column and display the value of the `name`
The following code prepares a constant named `identityEntityPropContributors`, ready to be imported and used in your root module:
```js
// entity-prop-contributors.ts
// src/app/entity-prop-contributors.ts
import {
eIdentityComponents,
IdentityEntityPropContributors,
IdentityUserDto,
} from '@abp/ng.identity';
import { EntityProp, EntityPropList, ePropType } from '@abp/ng.theme.shared/extensions';
import { IdentityEntityPropContributors, IdentityUserDto } from '@volo/abp.ng.identity';
const nameProp = new EntityProp<IdentityUserDto>({
type: ePropType.String,
@ -32,62 +36,45 @@ const nameProp = new EntityProp<IdentityUserDto>({
});
export function namePropContributor(propList: EntityPropList<IdentityUserDto>) {
propList.addAfter(
nameProp,
'userName',
(value, name) => value.name === name,
);
propList.addAfter(nameProp, 'userName', (value, name) => value.name === name);
}
export const identityEntityPropContributors: IdentityEntityPropContributors = {
'Identity.UsersComponent': [namePropContributor],
// enum indicates the page to add contributors to
[eIdentityComponents.Users]: [
namePropContributor,
// You can add more contributors here
],
};
```
The list of props, conveniently named as `propList`, is a **doubly linked list**. That is why we have used the `addAfter` method, which adds a node with given value after the first node that has the previous value. You may find [all available methods here](../Common/Utils/Linked-List.md).
> **Important Note 1:** AoT compilation does not support function calls in decorator metadata. This is why we have defined `namePropContributor` as an exported function declaration here. Please do not forget exporting your contributor callbacks and forget about lambda functions (a.k.a. arrow functions). Please refer to [AoT metadata errors](https://angular.io/guide/aot-metadata-errors#function-calls-not-supported) for details.
> **Important Note 2:** Please use one of the following if Ivy is not enabled in your project. Otherwise, you will get an "Expression form not supported." error.
```js
export const identityEntityPropContributors: IdentityEntityPropContributors = {
'Identity.UsersComponent': [ namePropContributor ],
};
/* OR */
const identityContributors: IdentityEntityPropContributors = {};
identityContributors[eIdentityComponents.Users] = [ namePropContributor ];
export const identityEntityPropContributors = identityContributors;
```
### Step 2. Import and Use Entity Prop Contributors
Import `identityEntityPropContributors` in your routing module and pass it to the static `forLazy` method of `IdentityModule` as seen below:
```js
// src/app/app-routing.module.ts
// other imports
import { identityEntityPropContributors } from './entity-prop-contributors';
const routes: Routes = [
// other routes
{
path: '',
component: DynamicLayoutComponent,
children: [
{
path: 'identity',
loadChildren: () =>
import('@volo/abp.ng.identity').then(m =>
m.IdentityModule.forLazy({
entityPropContributors: identityEntityPropContributors,
}),
),
},
// other child routes
],
// other routes
}
path: 'identity',
loadChildren: () =>
import('@abp/ng.identity').then(m =>
m.IdentityModule.forLazy({
entityPropContributors: identityEntityPropContributors,
})
),
},
// other routes
];
```
@ -97,12 +84,18 @@ That is it, `nameProp` entity prop will be added, and you will see the "Name" co
You can use the `valueResolver` to render an HTML string in the table. Imagine we want to show a red times icon (❌) next to unconfirmed emails and phones, instead of showing a green check icon next to confirmed emails and phones. The contributors below would do that for you.
<img alt="Entity Prop Extension Example: Custom Cell Render" src="./images/entity-prop-extensions---custom-cell.gif" width="800px" style="max-width:100%">
```js
// entity-prop-contributors.ts
// src/app/entity-prop-contributors.ts
import { EntityProp, EntityPropList, ePropType } from '@abp/ng.theme.shared/extensions';
import { IdentityUserDto } from '@volo/abp.ng.identity';
import { IdentityEntityPropContributors } from '@volo/abp.ng.identity/config';
import {
eIdentityComponents,
IdentityEntityPropContributors,
IdentityUserDto,
} from '@abp/ng.identity';
import { EntityProp, EntityPropList } from '@abp/ng.theme.shared/extensions';
import { of } from 'rxjs';
export function emailPropContributor(propList: EntityPropList<IdentityUserDto>) {
const index = propList.indexOf('email', (value, name) => value.name === name);
@ -138,7 +131,7 @@ export function phonePropContributor(propList: EntityPropList<IdentityUserDto>)
}
export const identityEntityPropContributors: IdentityEntityPropContributors = {
'Identity.UsersComponent': [emailPropContributor, phonePropContributor],
[eIdentityComponents.Users]: [emailPropContributor, phonePropContributor],
};
```

74
docs/en/UI/Angular/Dynamic-Form-Extensions.md

@ -5,7 +5,7 @@
Form prop extension system allows you to add a new field to the create and/or edit forms for a form or change/remove an already existing one. A "Date of Birth" field was added to the user management page below:
![Form Prop Extension Example: "Date of Birth" Field](images/user-prop-extension-date-of-birth-field-ng.png)
<img alt="Form Prop Extension Example: 'Date of Birth' Field" src="./images/form-prop-extensions---birthday-field.gif" width="800px" style="max-width:100%">
You can validate the field, perform visibility checks, and do more. You will also have access to the current entity when creating a contibutor for an edit form.
@ -18,16 +18,20 @@ In this example, we will add a "Date of Birth" field in the user management page
The following code prepares two constants named `identityCreateFormPropContributors` and `identityEditFormPropContributors`, ready to be imported and used in your root module:
```js
// form-prop-contributors.ts
// src/app/form-prop-contributors.ts
import { Validators } from '@angular/forms';
import {
eIdentityComponents,
IdentityCreateFormPropContributors,
IdentityUserDto,
} from '@abp/ng.identity';
import { ePropType, FormProp, FormPropList } from '@abp/ng.theme.shared/extensions';
import { IdentityCreateFormPropContributors, IdentityEditFormPropContributors, IdentityUserDto } from '@volo/abp.ng.identity';
import { Validators } from '@angular/forms';
const birthdayProp = new FormProp<IdentityUserDto>({
type: ePropType.Date,
name: 'birthday',
displayName: 'Date of Birth',
displayName: 'AbpIdentity::Birthday',
validators: () => [Validators.required],
});
@ -36,63 +40,49 @@ export function birthdayPropContributor(propList: FormPropList<IdentityUserDto>)
}
export const identityCreateFormPropContributors: IdentityCreateFormPropContributors = {
'Identity.UsersComponent': [birthdayPropContributor],
// enum indicates the page to add contributors to
[eIdentityComponents.Users]: [
birthdayPropContributor,
// You can add more contributors here
],
};
export const identityEditFormPropContributors: IdentityEditFormPropContributors = {
'Identity.UsersComponent': [birthdayPropContributor],
};
export const identityEditFormPropContributors = identityCreateFormPropContributors;
// you may define different contributors for edit form if you like
```
The list of props, conveniently named as `propList`, is a **doubly linked list**. That is why we have used the `addByIndex` method, which adds the given value to the specified index of the list. You may find [all available methods here](../Common/Utils/Linked-List.md).
> **Important Note 1:** AoT compilation does not support function calls in decorator metadata. This is why we have defined `birthdayPropContributor` as an exported function declaration here. Please do not forget exporting your contributor callbacks and forget about lambda functions (a.k.a. arrow functions). Please refer to [AoT metadata errors](https://angular.io/guide/aot-metadata-errors#function-calls-not-supported) for details.
> **Important Note 2:** Please use one of the following if Ivy is not enabled in your project. Otherwise, you will get an "Expression form not supported." error.
```js
export const identityCreateFormPropContributors: IdentityCreateFormPropContributors = {
'Identity.UsersComponent': [ birthdayPropContributor ],
};
/* OR */
const identityCreateContributors: IdentityCreateFormPropContributors = {};
identityCreateContributors[eIdentityComponents.Users] = [ birthdayPropContributor ];
export const identityCreateFormPropContributors = identityCreateContributors;
```
### Step 2. Import and Use Form Prop Contributors
Import `identityCreateFormPropContributors` and `identityEditFormPropContributors` in your routing module and pass it to the static `forLazy` method of `IdentityModule` as seen below:
```js
// src/app/app-routing.module.ts
// other imports
import {
identityCreateFormPropContributors,
identityEditFormPropContributors,
} from './form-prop-contributors';
const routes: Routes = [
// other routes
{
path: '',
component: DynamicLayoutComponent,
children: [
{
path: 'identity',
loadChildren: () =>
import('@volo/abp.ng.identity').then(m =>
m.IdentityModule.forLazy({
createFormPropContributors: identityCreateFormPropContributors,
editFormPropContributors: identityEditFormPropContributors,
}),
),
},
// other child routes
],
// other routes
}
path: 'identity',
loadChildren: () =>
import('@abp/ng.identity').then(m =>
m.IdentityModule.forLazy({
createFormPropContributors: identityCreateFormPropContributors,
editFormPropContributors: identityEditFormPropContributors,
})
),
},
// other routes
];
```

320
docs/en/UI/Angular/Entity-Action-Extensions.md

@ -4,9 +4,9 @@
Entity action extension system allows you to add a new action to the action menu for an entity. A "Click Me" action was added to the user management page below:
![Entity Action Extension Example: "Click Me!" Action](images/user-action-extension-click-me-ng.png)
<img alt="Entity Action Extension Example: 'Click Me!' Action" src="./images/entity-action-extensions---click-me.gif" width="800px" style="max-width:100%">
You can take any action (open a modal, make an HTTP API call, redirect to another page... etc) by writing your custom code. You can access to the current entity in your code.
You can take any action (open a modal, make an HTTP API call, redirect to another page... etc) by writing your custom code. You can also access the current entity in your code.
## How to Set Up
@ -17,10 +17,14 @@ In this example, we will add a "Click Me!" action and alert the current row's `u
The following code prepares a constant named `identityEntityActionContributors`, ready to be imported and used in your root module:
```js
// entity-action-contributors.ts
// src/app/entity-action-contributors.ts
import {
eIdentityComponents,
IdentityEntityActionContributors,
IdentityUserDto,
} from '@abp/ng.identity';
import { EntityAction, EntityActionList } from '@abp/ng.theme.shared/extensions';
import { IdentityEntityActionContributors, IdentityUserDto } from '@volo/abp.ng.identity';
const alertUserName = new EntityAction<IdentityUserDto>({
text: 'Click Me!',
@ -31,9 +35,7 @@ const alertUserName = new EntityAction<IdentityUserDto>({
// See EntityActionOptions in API section for all options
});
export function alertUserNameContributor(
actionList: EntityActionList<IdentityUserDto>,
) {
export function alertUserNameContributor(actionList: EntityActionList<IdentityUserDto>) {
actionList.addTail(alertUserName);
}
@ -48,243 +50,199 @@ export const identityEntityActionContributors: IdentityEntityActionContributors
The list of actions, conveniently named as `actionList`, is a **doubly linked list**. That is why we have used the `addTail` method, which adds the given value to the end of the list. You may find [all available methods here](../Common/Utils/Linked-List.md).
> **Important Note 1:** AoT compilation does not support function calls in decorator metadata. This is why we have defined `alertUserNameContributor` as an exported function declaration here. Please do not forget exporting your contributor callbacks and forget about lambda functions (a.k.a. arrow functions). Please refer to [AoT metadata errors](https://angular.io/guide/aot-metadata-errors#function-calls-not-supported) for details.
> **Important Note 2:** Please use one of the following if Ivy is not enabled in your project. Otherwise, you will get an "Expression form not supported." error.
```js
export const identityEntityActionContributors: IdentityEntityActionContributors = {
'Identity.UsersComponent': [ alertUserNameContributor ],
};
/* OR */
const identityContributors: IdentityEntityActionContributors = {};
identityContributors[eIdentityComponents.Users] = [ alertUserNameContributor ];
export const identityEntityActionContributors = identityContributors;
```
### Step 2. Import and Use Entity Action Contributors
Import `identityEntityActionContributors` in your routing module and pass it to the static `forLazy` method of `IdentityModule` as seen below:
```js
// src/app/app-routing.module.ts
// other imports
import { identityEntityActionContributors } from './entity-action-contributors';
const routes: Routes = [
// other routes
{
path: '',
component: DynamicLayoutComponent,
children: [
{
path: 'identity',
loadChildren: () =>
import('@volo/abp.ng.identity').then(m =>
m.IdentityModule.forLazy({
entityActionContributors: identityEntityActionContributors,
}),
),
},
// other child routes
],
// other routes
}
path: 'identity',
loadChildren: () =>
import('@abp/ng.identity').then(m =>
m.IdentityModule.forLazy({
entityActionContributors: identityEntityActionContributors,
})
),
},
// other routes
];
```
That is it, `alertUserName` entity action will be added as the last action on the grid dropdown in the users page (`UsersComponent`) of the `IdentityModule`.
That is it, `alertUserName` entity action will be added as the last action on the grid dropdown in the "Users" page (`UsersComponent`) of the `IdentityModule`.
## How to Place a Custom Modal and Trigger It by Entity Actions
Incase you need to place a custom modal that will be triggered by an entity action, there are two ways to do it: A quick one and an elaborate one.
Let's employ dependency injection to extend the functionality of `IdentityModule` and add a quick view action for the User entity. We will take a lazy-loaded approach.
### The Quick Solution
<img alt="Entity Action Extension Example: Custom Modal" src="./images/entity-action-extensions---custom-modal.gif" width="800px" style="max-width:100%">
1. Place your custom modal inside `AppComponent` template.
```html
<abp-modal [(visible)]="isModalOpen">
<ng-template #abpHeader>
<h3><!-- YOUR TITLE HERE --></h3>
</ng-template>
1. Create a folder at this path: `src/app/identity-extended`
<ng-template #abpBody>
<!-- YOUR CONTENT HERE -->
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
{%{{{ 'AbpIdentity::Cancel' | abpLocalization }}}%}
</button>
<!-- YOUR CONFIRMATION BUTTON HERE -->
</ng-template>
</abp-modal>
```
2. Add the following inside your `AppComponent` class:
```js
isModalOpen: boolean;
openModal(/* may take parameters */) {
/* and set things before showing the modal */
this.isModalOpen = true;
}
```
3. Add an entity action similar to this:
2. Add an entity action similar to this:
```js
const customModalAction = new EntityAction<IdentityUserDto>({
text: 'Custom Modal Action',
// src/app/identity-extended/entity-action-contributors.ts
import {
eIdentityComponents,
IdentityEntityActionContributors,
IdentityUserDto,
} from '@abp/ng.identity';
import { EntityAction, EntityActionList } from '@abp/ng.theme.shared/extensions';
import { IdentityExtendedComponent } from './identity-extended.component';
const quickViewAction = new EntityAction<IdentityUserDto>({
text: 'Quick View',
action: data => {
const component = data.getInjected(AppComponent);
component.openModal(/* you may pass parameters */);
const component = data.getInjected(IdentityExtendedComponent);
component.openUserQuickView(data.record);
},
});
```
That should work. However, there is a longer but lazy-loading solution, and we are going to use NGXS for it.
### The Elaborate Solution
export function customModalContributor(actionList: EntityActionList<IdentityUserDto>) {
actionList.addTail(quickViewAction);
}
Consider the modal will be displayed in the Identity module. How can we lazy-load it too?
export const identityEntityActionContributors: IdentityEntityActionContributors = {
// enum indicates the page to add contributors to
[eIdentityComponents.Users]: [
customModalContributor,
// You can add more contributors here
],
};
```
1. Create a folder called `identity-extended` inside your app folder.
2. Create a file called `identity-popups.store.ts` in it.
3. Insert the following code in the new file:
3. Create a parent component to the identity module.
```js
import { Action, Selector, State, StateContext } from '@ngxs/store';
// src/app/identity-extended/identity-extended.component.ts
export class ToggleIdentityPopup {
static readonly type = '[IdentityPopups] Toggle';
constructor(public readonly payload: boolean) {}
}
import { IdentityUserDto } from '@abp/ng.identity';
import { Component } from '@angular/core';
@State<IdentityPopupsStateModel>({
name: 'IdentityPopups',
defaults: {
isVisible: false,
},
@Component({
selector: 'app-identity-extended',
templateUrl: './identity-extended.component.html',
})
export class IdentityPopupsState {
@Selector()
static isVisible(state: IdentityPopupsStateModel) {
return state.isVisible;
}
export class IdentityExtendedComponent {
isUserQuickViewVisible: boolean;
user: IdentityUserDto;
@Action(ToggleIdentityPopup)
toggleModal(
context: StateContext<IdentityPopupsStateModel>,
{ payload }: ToggleIdentityPopup,
) {
context.patchState({ isVisible: payload });
openUserQuickView(record: IdentityUserDto) {
this.user = new Proxy(record, {
get: (target, prop) => target[prop] || '—',
});
this.isUserQuickViewVisible = true;
}
}
```
interface IdentityPopupsStateModel {
isVisible: boolean;
}
4. Add a router outlet and a modal to the parent component.
```html
<!-- src/app/identity-extended/identity-extended.component.html -->
<router-outlet></router-outlet>
<abp-modal [(visible)]="isUserQuickViewVisible">
<ng-template #abpHeader>
<h3>{%{{{ user.userName }}}%}</h3>
</ng-template>
<ng-template #abpBody>
<table class="table table-borderless">
<tbody>
<tr>
<th scope="row">{%{{{ 'AbpIdentity::DisplayName:Name' | abpLocalization }}}%}</th>
<td>{%{{{ user.name }}}%}</td>
</tr>
<tr>
<th scope="row">{%{{{ 'AbpIdentity::DisplayName:Surname' | abpLocalization }}}%}</th>
<td>{%{{{ user.surname }}}%}</td>
</tr>
<tr>
<th scope="row">{%{{{ 'AbpIdentity::EmailAddress' | abpLocalization }}}%}</th>
<td>{%{{{ user.email }}}%}</td>
</tr>
<tr>
<th scope="row">{%{{{ 'AbpIdentity::PhoneNumber' | abpLocalization }}}%}</th>
<td>{%{{{ user.phoneNumber }}}%}</td>
</tr>
</tbody>
</table>
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
{%{{{ 'AbpUi::Close' | abpLocalization }}}%}
</button>
</ng-template>
</abp-modal>
```
4. Create a file called `identity-extended.module.ts` in the same folder.
5. Insert the following code in the new file:
5. Add a module for the component and load `IdentityModule` as seen below:
```js
// src/app/identity-extended/identity-extended.module.ts
import { CoreModule } from '@abp/ng.core';
import { IdentityModule } from '@abp/ng.identity';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { Component, NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { NgxsModule, Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { IdentityPopupsState, ToggleIdentityPopup } from './identity-popups.store';
@Component({
template: `
<router-outlet></router-outlet>
<router-outlet name="popup"></router-outlet>
`,
})
export class IdentityOutletComponent {}
@Component({
template: `
<abp-modal [visible]="isVisible$ | async" (disappear)="onDisappear()">
<ng-template #abpHeader>
<h3><!-- YOUR TITLE HERE --></h3>
</ng-template>
<ng-template #abpBody>
<!-- YOUR CONTENT HERE -->
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
{%{{{ 'AbpIdentity::Cancel' | abpLocalization }}}%}
</button>
<!-- YOUR CONFIRMATION BUTTON HERE -->
</ng-template>
</abp-modal>
`,
})
export class IdentityPopupsComponent {
@Select(IdentityPopupsState.isVisible)
isVisible$: Observable<boolean>;
constructor(private store: Store) {}
onDisappear() {
this.store.dispatch(new ToggleIdentityPopup(false));
}
}
import { identityEntityActionContributors } from './entity-action-contributors';
import { IdentityExtendedComponent } from './identity-extended.component';
@NgModule({
declarations: [IdentityPopupsComponent, IdentityOutletComponent],
imports: [
CoreModule,
ThemeSharedModule,
NgxsModule.forFeature([IdentityPopupsState]),
RouterModule.forChild([
{
path: '',
component: IdentityOutletComponent,
component: IdentityExtendedComponent,
children: [
{
path: '',
outlet: 'popup',
component: IdentityPopupsComponent,
},
{
path: '',
loadChildren: () => import('@volo/abp.ng.identity').then(m => m.IdentityModule),
loadChildren: () =>
IdentityModule.forLazy({
entityActionContributors: identityEntityActionContributors,
}),
},
],
},
]),
],
declarations: [IdentityExtendedComponent],
})
export class IdentityExtendedModule {}
```
6. Change the `identity` path in your `AppRoutingModule` to this:
6. Load `IdentityExtendedModule` instead of `IdentityModule` in your root routing module.
```js
{
path: 'identity',
loadChildren: () =>
import('./identity-extended/identity-extended.module').then(m => m.IdentityExtendedModule),
},
```
// src/app/app-routing.module.ts
7. Add an entity action similar to this:
```js
const customModalAction = new EntityAction<IdentityUserDto>({
text: 'Custom Modal Action',
action: data => {
const store = data.getInjected(Store);
store.dispatch(new ToggleIdentityPopup(true));
const routes: Routes = [
// other routes
{
path: 'identity',
loadChildren: () =>
import('./identity-extended/identity-extended.module')
.then(m => m.IdentityExtendedModule),
},
});
// other routes
];
```
It should now be working well with lazy-loading. The files are compact in the description to make it quicker to explain. You may split the files as you wish.
That's it. As you see, we reached the `IdentityExtendedComponent` through dependency injection and called one of its methods in our action. The specific user was also available via `data.record`, so we were able to display a summary view.
## API
@ -374,7 +332,7 @@ You may find a full example below.
```js
const options: EntityActionOptions<IdentityUserDto> = {
action: data => {
const component = data.getInjected(UsersComponent);
const component = data.getInjected(IdentityExtendedComponent);
component.unlock(data.record.id);
},
text: 'AbpIdentity::Unlock',

126
docs/en/UI/Angular/Page-Toolbar-Extensions.md

@ -4,7 +4,7 @@
Page toolbar extension system allows you to add a new action to the toolbar of a page. A "Click Me" action was added to the user management page below:
![Page Toolbar Extension Example: "Click Me!" Action](images/user-page-toolbar-extension-click-me-ng.png)
<img alt="Page Toolbar Extension Example: 'Click Me!' Action" src="./images/toolbar-action-extensions---click-me.gif" width="800px" style="max-width:100%">
You can take any action (open a modal, make an HTTP API call, redirect to another page... etc) by writing your custom code. You can also access to page data (the main record, usually an entity list) in your code. Additionally, you can pass in custom components instead of using the default button.
@ -17,10 +17,14 @@ In this example, we will add a "Click Me!" action and log `userName` of all user
The following code prepares a constant named `identityToolbarActionContributors`, ready to be imported and used in your root module:
```js
// toolbar-action-contributors.ts
// src/app/toolbar-action-contributors.ts
import { ToolbarActionList, ToolbarAction } from '@abp/ng.theme.shared/extensions';
import { IdentityToolbarActionContributors, IdentityUserDto } from '@volo/abp.ng.identity';
import {
eIdentityComponents,
IdentityToolbarActionContributors,
IdentityUserDto,
} from '@abp/ng.identity';
import { ToolbarAction, ToolbarActionList } from '@abp/ng.theme.shared/extensions';
const logUserNames = new ToolbarAction<IdentityUserDto[]>({
text: 'Click Me!',
@ -31,9 +35,7 @@ const logUserNames = new ToolbarAction<IdentityUserDto[]>({
// See ToolbarActionOptions in API section for all options
});
export function logUserNamesContributor(
actionList: ToolbarActionList<IdentityUserDto[]>
) {
export function logUserNamesContributor(actionList: ToolbarActionList<IdentityUserDto[]>) {
actionList.addHead(logUserNames);
}
@ -49,33 +51,30 @@ export const identityToolbarActionContributors: IdentityToolbarActionContributor
The list of actions, conveniently named as `actionList`, is a **doubly linked list**. That is why we have used the `addHead` method, which adds the given value to the beginning of the list. You may find [all available methods here](../Common/Utils/Linked-List.md).
> **Important Note:** AoT compilation does not support function calls in decorator metadata. This is why we have defined `logUserNamesContributor` as an exported function declaration here. Please do not forget exporting your contributor callbacks and forget about lambda functions (a.k.a. arrow functions). Please refer to [AoT metadata errors](https://angular.io/guide/aot-metadata-errors#function-calls-not-supported) for details.
### Step 2. Import and Use Toolbar Action Contributors
Import `identityToolbarActionContributors` in your routing module and pass it to the static `forLazy` method of `IdentityModule` as seen below:
```js
// src/app/app-routing.module.ts
// other imports
import { identityToolbarActionContributors } from './toolbar-action-contributors';
const routes: Routes = [
// other routes
{
path: '',
component: DynamicLayoutComponent,
children: [
{
path: 'identity',
loadChildren: () =>
import('@volo/abp.ng.identity').then(m =>
m.IdentityModule.forLazy({
toolbarActionContributors: identityToolbarActionContributors,
}),
),
},
// other child routes
],
// other routes
}
path: 'identity',
loadChildren: () =>
import('@abp/ng.identity').then(m =>
m.IdentityModule.forLazy({
toolbarActionContributors: identityToolbarActionContributors,
})
),
},
// other routes
];
```
@ -85,22 +84,22 @@ That is it, `logUserNames` toolbar action will be added as the first action on t
In this example, we will add a custom "Click Me!" button and log `userName` of all users in the user management page of the [Identity Module](../../Modules/Identity.md) to the console.
<img alt="Page Toolbar Extension Example: Custom Component" src="./images/toolbar-action-extensions---custom-component.gif" width="800px" style="max-width:100%">
### Step 1. Create A Custom Component
We need to have a component before we can pass it to the toolbar action contributors:
```js
// click-me-button.component.ts
// src/app/click-me-button.component.ts
import { Component, Inject } from '@angular/core';
import { IdentityUserDto } from '@abp/ng.identity';
import { ActionData, EXTENSIONS_ACTION_DATA } from '@abp/ng.theme.shared/extensions';
import { IdentityUserDto } from '@volo/abp.ng.identity';
import { Component, Inject } from '@angular/core';
@Component({
selector: 'app-click-me-button',
template: `
<button class="btn btn-warning" (click)="handleClick()">Click Me!</button>
`,
template: `<button class="btn btn-warning" (click)="handleClick()">Click Me!</button>`,
})
export class ClickMeButtonComponent {
constructor(
@ -112,6 +111,7 @@ export class ClickMeButtonComponent {
this.data.record.forEach(user => console.log(user.userName));
}
}
```
Here, `EXTENSIONS_ACTION_DATA` token provides us the context from the page toolbar. Therefore, we are able to reach the page data via `record`, which is an array of users, i.e. `IdentityUserDto[]`.
@ -123,11 +123,14 @@ Here, `EXTENSIONS_ACTION_DATA` token provides us the context from the page toolb
The following code prepares a constant named `identityToolbarActionContributors`, ready to be imported and used in your root module. When `ToolbarComponent` is used instead of `ToolbarAction`, we can pass a component in:
```js
// toolbar-action-contributors.ts
// src/app/toolbar-action-contributors.ts
import {
eIdentityComponents,
IdentityToolbarActionContributors,
IdentityUserDto,
} from '@abp/ng.identity';
import { ToolbarActionList, ToolbarComponent } from '@abp/ng.theme.shared/extensions';
import { IdentityUserDto } from '@volo/abp.ng.identity';
import { IdentityToolbarActionContributors } from '@volo/abp.ng.identity/config';
import { ClickMeButtonComponent } from './click-me-button.component';
const logUserNames = new ToolbarComponent<IdentityUserDto[]>({
@ -135,9 +138,7 @@ const logUserNames = new ToolbarComponent<IdentityUserDto[]>({
// See ToolbarActionOptions in API section for all options
});
export function logUserNamesContributor(
actionList: ToolbarActionList<IdentityUserDto[]>
) {
export function logUserNamesContributor(actionList: ToolbarActionList<IdentityUserDto[]>) {
actionList.addHead(logUserNames);
}
@ -153,54 +154,35 @@ export const identityToolbarActionContributors: IdentityToolbarActionContributor
The list of actions, conveniently named as `actionList`, is a **doubly linked list**. That is why we have used the `addHead` method, which adds the given value to the beginning of the list. You may find [all available methods here](../Common/Utils/Linked-List.md).
> **Important Note 1:** AoT compilation does not support function calls in decorator metadata. This is why we have defined `logUserNamesContributor` as an exported function declaration here. Please do not forget exporting your contributor callbacks and forget about lambda functions (a.k.a. arrow functions). Please refer to [AoT metadata errors](https://angular.io/guide/aot-metadata-errors#function-calls-not-supported) for details.
> **Important Note 2:** Please use one of the following if Ivy is not enabled in your project. Otherwise, you will get an "Expression form not supported." error.
```js
export const identityToolbarActionContributors: IdentityToolbarActionContributors = {
'Identity.UsersComponent': [ logUserNamesContributor ],
};
/* OR */
const identityContributors: IdentityToolbarActionContributors = {};
identityContributors[eIdentityComponents.Users] = [ logUserNamesContributor ];
export const identityToolbarActionContributors = identityContributors;
```
### Step 3. Import and Use Toolbar Action Contributors
Import `identityToolbarActionContributors` in your routing module and pass it to the static `forLazy` method of `IdentityModule` as seen below. If Ivy is not enabled in your project, do not forget putting `ClickMeButtonComponent` into `entryComponents`:
Import `identityToolbarActionContributors` in your routing module and pass it to the static `forLazy` method of `IdentityModule` as seen below.
```js
// src/app/app-routing.module.ts
// other imports
import { identityToolbarActionContributors } from './toolbar-action-contributors';
const routes: Routes = [
// other routes
{
path: '',
component: DynamicLayoutComponent,
children: [
{
path: 'identity',
loadChildren: () =>
import('@volo/abp.ng.identity').then(m =>
m.IdentityModule.forLazy({
toolbarActionContributors: identityToolbarActionContributors,
}),
),
},
// other child routes
],
// other routes
}
path: 'identity',
loadChildren: () =>
import('@abp/ng.identity').then(m =>
m.IdentityModule.forLazy({
toolbarActionContributors: identityToolbarActionContributors,
})
),
},
// other routes
];
```
That is it, `logUserNames` toolbar action will be added as the first action on the page toolbar in the users page (`UsersComponent`) of the `IdentityModule` and it will be triggered by a custom button, i.e. `ClickMeButtonComponent`. Please note that **component projection is not limited to buttons** and you may use other UI components.
![Page Toolbar Extension Example: Custom "Click Me!" Button](images/user-page-toolbar-extension-custom-click-me-ng.png)
## How to Place a Custom Modal and Trigger It by Toolbar Actions
Please check the same topic in [entity action extensions document](Entity-Action-Extensions.md) and replace entity action with a toolbar action.

BIN
docs/en/UI/Angular/images/entity-action-extensions---click-me.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

BIN
docs/en/UI/Angular/images/entity-action-extensions---custom-modal.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

BIN
docs/en/UI/Angular/images/entity-prop-extensions---custom-cell.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 KiB

BIN
docs/en/UI/Angular/images/entity-prop-extensions---name-column.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 KiB

BIN
docs/en/UI/Angular/images/form-prop-extensions---birthday-field.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 KiB

BIN
docs/en/UI/Angular/images/toolbar-action-extensions---click-me.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
docs/en/UI/Angular/images/toolbar-action-extensions---custom-component.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Loading…
Cancel
Save