# How Replaceable Components Work with Extensions Additional UI extensibility points ([Entity action extensions](../angular/entity-action-extensions.md), [data table column extensions](../angular/data-table-column-extensions.md), [page toolbar extensions](../angular/page-toolbar-extensions.md) and others) are used in ABP pages to allow to control entity actions, table columns and page toolbar of a page. If you replace a page, you need to apply some configurations to be able to work extension components in your component. Let's see how to do this by replacing the roles page. Create a new module called `MyRolesModule`: ```bash yarn ng generate module my-roles --module app ``` Create a new component called `MyRolesComponent`: ```bash yarn ng generate component my-roles/my-roles --flat --export ``` Open the generated `src/app/my-roles/my-roles.component.ts` file and replace its content with the following: ```js import { Component, Injector, inject, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { finalize } from 'rxjs/operators'; import { ListService, PagedAndSortedResultRequestDto, PagedResultDto } from '@abp/ng.core'; import { eIdentityComponents, RolesComponent } from '@abp/ng.identity'; import { IdentityRoleDto, IdentityRoleService } from '@abp/ng.identity/proxy'; import { ePermissionManagementComponents } from '@abp/ng.permission-management'; import { Confirmation, ConfirmationService } from '@abp/ng.theme.shared'; import { EXTENSIONS_IDENTIFIER, FormPropData, generateFormFromProps } from '@abp/ng.components/extensible'; @Component({ selector: 'app-my-roles', templateUrl: './my-roles.component.html', providers: [ ListService, { provide: EXTENSIONS_IDENTIFIER, useValue: eIdentityComponents.Roles, }, { provide: RolesComponent, useExisting: MyRolesComponent, }, ], }) export class MyRolesComponent implements OnInit { public readonly list = inject>(ListService); protected readonly confirmationService = inject(ConfirmationService); protected readonly injector = inject(Injector); protected readonly service = inject(IdentityRoleService); data: PagedResultDto = { items: [], totalCount: 0 }; form: FormGroup; selected: IdentityRoleDto; isModalVisible: boolean; visiblePermissions = false; providerKey: string; modalBusy = false; permissionManagementKey = ePermissionManagementComponents.PermissionManagement; onVisiblePermissionChange = (event) => { this.visiblePermissions = event; }; ngOnInit() { this.hookToQuery(); } buildForm() { const data = new FormPropData(this.injector, this.selected); this.form = generateFormFromProps(data); } openModal() { this.buildForm(); this.isModalVisible = true; } add() { this.selected = {} as IdentityRoleDto; this.openModal(); } edit(id: string) { this.service.get(id).subscribe(res => { this.selected = res; this.openModal(); }); } save() { if (!this.form.valid) return; this.modalBusy = true; const { id } = this.selected; (id ? this.service.update(id, { ...this.selected, ...this.form.value }) : this.service.create(this.form.value) ) .pipe(finalize(() => (this.modalBusy = false))) .subscribe(() => { this.isModalVisible = false; this.list.get(); }); } delete(id: string, name: string) { this.confirmationService .warn('AbpIdentity::RoleDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', { messageLocalizationParams: [name], }) .subscribe((status: Confirmation.Status) => { if (status === Confirmation.Status.confirm) { this.service.delete(id).subscribe(() => this.list.get()); } }); } private hookToQuery() { this.list.hookToQuery(query => this.service.getList(query)).subscribe(res => (this.data = res)); } openPermissionsModal(providerKey: string) { this.providerKey = providerKey; setTimeout(() => { this.visiblePermissions = true; }, 0); } sort(data) { const { prop, dir } = data.sorts[0]; this.list.sortKey = prop; this.list.sortOrder = dir; } } ``` ```js { provide: EXTENSIONS_IDENTIFIER, useValue: eIdentityComponents.Roles, }, { provide: RolesComponent, useExisting: MyRolesComponent } ``` The two providers we have defined in `MyRolesComponent` are required for the extension components to work correctly. * With the first provider, we defined the extension identifier for using `RolesComponent`'s extension actions in the `MyRolesComponent`. * With the second provider, we have replaced the `RolesComponent` injection with the `MyRolesComponent`. Default extension actions of the `RolesComponent` try to get `RolesComponent` instance. However, the actions can get the `MyRolesComponent` instance after defining the second provider. Open the generated `src/app/my-role/my-role.component.html` file and replace its content with the following: ```html
My Roles

{%{{{ (selected?.id ? 'AbpIdentity::Edit' : 'AbpIdentity::NewRole') | abpLocalization }}}%}

{%{{{ 'AbpIdentity::Save' | abpLocalization }}}%}
``` We have added the `abp-page-toolbar`, `abp-extensible-table`, and `abp-extensible-form` extension components to template of the `MyRolesComponent`. You should import the required modules for the `MyRolesComponent` to `MyRolesModule`. Open the `src/my-roles/my-roles.module.ts` file and replace the content with the following: ```js import { ExtensibleModule } from '@abp/ng.components/extensible'; import { NgModule } from '@angular/core'; import { SharedModule } from '../shared/shared.module'; import { MyRolesComponent } from './my-roles.component'; import { PermissionManagementModule } from '@abp/ng.permission-management'; @NgModule({ declarations: [MyRolesComponent], imports: [SharedModule, ExtensibleModule, PermissionManagementModule], exports: [MyRolesComponent], }) export class MyRolesModule {} ``` - `ExtensionsModule` imported to be able to use the extension components in your component. - `PermissionManagementModule` imported to be able to use the `abp-permission-*management` in your component. As the last step, it is needs to be replaced the `RolesComponent` with the `MyRolesComponent`. Open the `app.component.ts` and modify its content as shown below: ```js import { Component, inject } from '@angular/core'; import { ReplaceableComponentsService } from '@abp/ng.core'; import { eIdentityComponents } from '@abp/ng.identity'; import { MyRolesComponent } from './my-roles/my-roles.component'; @Component({ // component metadata }) export class AppComponent { private replaceableComponents = inject(ReplaceableComponentsService); constructor() { this.replaceableComponents.add({ component: MyRolesComponent, key: eIdentityComponents.Roles }); } } ``` After the steps above, the `RolesComponent` has been successfully replaced with the `MyRolesComponent`. When you navigate to the `/identity/roles` URL, you will see the `MyRolesComponent`'s template and see the extension components working correctly. ![my-roles-component-with-extensions](./images/my-roles-component-with-extensions.jpg) ![my-roles-component-form-extensions](./images/my-roles-component-form-extensions.jpg)