diff --git a/docs/en/UI/Angular/Entity-Action-Extensions.md b/docs/en/UI/Angular/Entity-Action-Extensions.md
index d46bd8e194..8918bc37a6 100644
--- a/docs/en/UI/Angular/Entity-Action-Extensions.md
+++ b/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:
-
+
-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({
text: 'Click Me!',
@@ -31,9 +35,7 @@ const alertUserName = new EntityAction({
// See EntityActionOptions in API section for all options
});
-export function alertUserNameContributor(
- actionList: EntityActionList,
-) {
+export function alertUserNameContributor(actionList: EntityActionList) {
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
+
-1. Place your custom modal inside `AppComponent` template.
- ```html
-
-
-
-
+1. Create a folder at this path: `src/app/identity-extended`
-
-
-
-
-
-
-
-
-
-
- ```
-
-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({
- 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({
+ 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) {
+ 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({
- 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,
- { 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
+
+
+
+
+
+
+
+
+
+
+
+
+
```
-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: `
-
-
- `,
- })
- export class IdentityOutletComponent {}
-
- @Component({
- template: `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `,
- })
- export class IdentityPopupsComponent {
- @Select(IdentityPopupsState.isVisible)
- isVisible$: Observable;
-
- 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({
- 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 = {
action: data => {
- const component = data.getInjected(UsersComponent);
+ const component = data.getInjected(IdentityExtendedComponent);
component.unlock(data.record.id);
},
text: 'AbpIdentity::Unlock',
diff --git a/docs/en/UI/Angular/images/entity-action-extensions---click-me.gif b/docs/en/UI/Angular/images/entity-action-extensions---click-me.gif
new file mode 100644
index 0000000000..6f5191874d
Binary files /dev/null and b/docs/en/UI/Angular/images/entity-action-extensions---click-me.gif differ
diff --git a/docs/en/UI/Angular/images/entity-action-extensions---custom-modal.gif b/docs/en/UI/Angular/images/entity-action-extensions---custom-modal.gif
new file mode 100644
index 0000000000..0b1010e029
Binary files /dev/null and b/docs/en/UI/Angular/images/entity-action-extensions---custom-modal.gif differ