项目

可替换组件如何与扩展协同工作

ABP 页面中使用了额外的 UI 扩展点(实体操作扩展数据表格列扩展页面工具栏扩展等),用于控制页面的实体操作、表格列和页面工具栏。如果您替换了某个页面,需要进行一些配置才能使扩展组件在您的组件中正常工作。让我们通过替换角色页面来看看具体如何操作。

创建一个名为 MyRolesModule 的新模块:

yarn ng generate module my-roles --module app

创建一个名为 MyRolesComponent 的新组件:

yarn ng generate component my-roles/my-roles --flat --export

打开生成的 src/app/my-roles/my-roles.component.ts 文件,并将其内容替换为以下代码:

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<PagedAndSortedResultRequestDto>>(ListService);
  protected readonly confirmationService = inject(ConfirmationService);
  protected readonly injector = inject(Injector);
  protected readonly service = inject(IdentityRoleService);

  data: PagedResultDto<IdentityRoleDto> = { 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;
  }
}
    {
      provide: EXTENSIONS_IDENTIFIER,
      useValue: eIdentityComponents.Roles,
    },
    { 
      provide: RolesComponent, 
      useExisting: MyRolesComponent 
    }

我们在 MyRolesComponent 中定义的这两个提供者是扩展组件正确工作所必需的。

  • 通过第一个提供者,我们定义了扩展标识符,以便在 MyRolesComponent 中使用 RolesComponent 的扩展操作。
  • 通过第二个提供者,我们用 MyRolesComponent 替换了 RolesComponent 注入。RolesComponent 的默认扩展操作尝试获取 RolesComponent 实例。然而,在定义了第二个提供者之后,这些操作可以获取 MyRolesComponent 实例。

打开生成的 src/app/my-role/my-role.component.html 文件,并将其内容替换为以下代码:

<div id="identity-roles-wrapper" class="card">
  <div class="card-header">
    <div class="row">
      <div class="col col-md-6">
        <h5 class="card-title">我的角色</h5>
      </div>
      <div class="text-end col col-md-6">
        <abp-page-toolbar [record]="data.items"></abp-page-toolbar>
      </div>
    </div>
  </div>

  <div class="card-body">
    <abp-extensible-table
      [data]="data.items"
      [recordsTotal]="data.totalCount"
      [list]="list"
    ></abp-extensible-table>
  </div>
</div>

<abp-modal size="md" [(visible)]="isModalVisible" [busy]="modalBusy">
  <ng-template #abpHeader>
    <h3>{{ (selected?.id ? 'AbpIdentity::Edit' : 'AbpIdentity::NewRole') | abpLocalization }}</h3>
  </ng-template>

  <ng-template #abpBody>
    <form [formGroup]="form" (ngSubmit)="save()" validateOnSubmit>
      <abp-extensible-form [selectedRecord]="selected"></abp-extensible-form>
    </form>
  </ng-template>

  <ng-template #abpFooter>
    <button type="button" class="btn btn-secondary" abpClose>
      {{ 'AbpIdentity::Cancel' | abpLocalization }}
    </button>
    <abp-button iconClass="fa fa-check" [disabled]="form?.invalid" (click)="save()">{{
      'AbpIdentity::Save' | abpLocalization
    }}</abp-button>
  </ng-template>
</abp-modal>

<abp-permission-management
  #abpPermissionManagement="abpPermissionManagement"
  *abpReplaceableTemplate="
    {
      inputs: {
        providerName: { value: 'R' },
        providerKey: { value: providerKey },
        visible: { value: visiblePermissions, twoWay: true },
        hideBadges: { value: true }
      },
      outputs: { visibleChange: onVisiblePermissionChange },
      componentKey: permissionManagementKey
    };
    let init = initTemplate
  "
  (abpInit)="init(abpPermissionManagement)"
>
</abp-permission-management>

我们已将 abp-page-toolbarabp-extensible-tableabp-extensible-form 扩展组件添加到 MyRolesComponent 的模板中。

您需要将 MyRolesComponent 所需的模块导入到 MyRolesModule 中。打开 src/my-roles/my-roles.module.ts 文件,并将其内容替换为以下代码:

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 以便在您的组件中使用扩展组件。
  • 导入 PermissionManagementModule 以便在您的组件中使用 abp-permission-*management

最后一步,需要用 MyRolesComponent 替换 RolesComponent。打开 app.component.ts 文件,并按如下所示修改其内容:

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({
  // 组件元数据
})
export class AppComponent {
  private replaceableComponents = inject(ReplaceableComponentsService);

  constructor() {
    this.replaceableComponents.add({ component: MyRolesComponent, key: eIdentityComponents.Roles });
  }
}

完成上述步骤后,RolesComponent 已成功被 MyRolesComponent 替换。当您导航到 /identity/roles URL 时,您将看到 MyRolesComponent 的模板,并看到扩展组件正常工作。

my-roles-component-with-extensions

my-roles-component-form-extensions

在本文档中