项目

Angular UI 页面工具栏扩展

简介

页面工具栏扩展系统允许您向页面的工具栏添加新操作。下图在用户管理页面添加了一个"点击我"操作:

您可以通过编写自定义代码来执行任何操作(打开模态框、发起HTTP API调用、重定向到其他页面等)。您还可以在代码中访问页面数据(主记录,通常是实体列表)。此外,您可以传入自定义组件来替代默认按钮。

如何向页面工具栏添加操作

在本示例中,我们将在身份模块的用户管理页面添加一个"点击我!"操作,并将所有用户的userName记录到控制台。

步骤 1. 创建工具栏操作贡献者

以下代码准备了一个名为identityToolbarActionContributors的常量,可供导入并在您的根应用程序配置中使用:

// src/app/toolbar-action-contributors.ts

import {
  eIdentityComponents,
  IdentityToolbarActionContributors
} from '@abp/ng.identity';
import { IdentityUserDto } from '@abp/ng.identity/proxy';
import { ToolbarAction, ToolbarActionList } from '@abp/ng.components/extensible';

const logUserNames = new ToolbarAction<IdentityUserDto[]>({
  text: '点击我!',
  action: data => {
    // 用您的自定义代码替换此日志记录
    data.record.forEach(user => console.log(user.userName));
  },
  // 所有选项请参见API部分中的ToolbarActionOptions
});

export function logUserNamesContributor(actionList: ToolbarActionList<IdentityUserDto[]>) {
  actionList.addHead(logUserNames);
}

export const identityToolbarActionContributors: IdentityToolbarActionContributors = {
  // 枚举表示要添加贡献者的页面
  [eIdentityComponents.Users]: [
    logUserNamesContributor,
    // 您可以在此处添加更多贡献者
  ],
};

操作列表(方便地命名为actionList)是一个双向链表。这就是我们使用addHead方法的原因,它将给定值添加到列表的开头。您可以在此处找到所有可用方法

步骤 2. 导入并使用工具栏操作贡献者

在您的路由配置中导入identityToolbarActionContributors,并将其传递给identity路由的静态createRoutes方法,如下所示:

// src/app/app.routes.ts

// 其他导入
import { identityToolbarActionContributors } from './toolbar-action-contributors';

export const APP_ROUTES: Routes = [
  // 其他路由

  {
    path: 'identity',
    loadChildren: () =>
      import('@abp/ng.identity').then(c =>
        c.createRoutes({
          toolbarActionContributors: identityToolbarActionContributors,
        })
      ),
  },

  // 其他路由
];

这样,logUserNames工具栏操作将作为identity包的users页面(UsersComponent)中页面工具栏上的第一个操作添加。

如何向页面工具栏添加自定义组件

在本示例中,我们将添加一个自定义的"点击我!"按钮,并将身份模块的用户管理页面中所有用户的userName记录到控制台。

步骤 1. 创建自定义组件

我们需要先创建一个组件,然后才能将其传递给工具栏操作贡献者:

// src/app/click-me-button.component.ts

import { Component, Inject } from '@angular/core';
import { IdentityUserDto } from '@abp/ng.identity/proxy';
import { ActionData, EXTENSIONS_ACTION_DATA } from '@abp/ng.components/extensible';

@Component({
  selector: 'app-click-me-button',
  template: `<button class="btn btn-warning" (click)="handleClick()">点击我!</button>`,
})
export class ClickMeButtonComponent {
  constructor(
    @Inject(EXTENSIONS_ACTION_DATA)
    private data: ActionData<IdentityUserDto[]>
  ) {}

  handleClick() {
    this.data.record.forEach(user => console.log(user.userName));
  }
}

这里,EXTENSIONS_ACTION_DATA令牌为我们提供了来自页面工具栏的上下文。因此,我们能够通过record访问页面数据,它是一个用户数组,即IdentityUserDto[]

我们还可以从 @abp/ng.components/extensible 包导入EXTENSIONS_ACTION_CALLBACK,它是一个高阶函数,调用时会触发预定义的action。它将ActionData作为第一个参数传递,因此您无需显式传递它。换句话说,EXTENSIONS_ACTION_CALLBACK可以在没有任何参数的情况下调用,并且不会失败。

步骤 2. 创建工具栏操作贡献者

以下代码准备了一个名为identityToolbarActionContributors的常量,可供导入并在您的根应用程序配置中使用。当使用ToolbarComponent而不是ToolbarAction时,我们可以传入一个组件:

// src/app/toolbar-action-contributors.ts

import {
  eIdentityComponents,
  IdentityToolbarActionContributors
} from '@abp/ng.identity';
import { IdentityUserDto } from '@abp/ng.identity/proxy';
import { ToolbarActionList, ToolbarComponent } from '@abp/ng.components/extensible';
import { ClickMeButtonComponent } from './click-me-button.component';

const logUserNames = new ToolbarComponent<IdentityUserDto[]>({
  component: ClickMeButtonComponent,
  // 所有选项请参见API部分中的ToolbarActionOptions
});

export function logUserNamesContributor(actionList: ToolbarActionList<IdentityUserDto[]>) {
  actionList.addHead(logUserNames);
}

export const identityToolbarActionContributors: IdentityToolbarActionContributors = {
  // 枚举表示要添加贡献者的页面
  [eIdentityComponents.Users]: [
    logUserNamesContributor,
    // 您可以在此处添加更多贡献者
  ],
};

操作列表(方便地命名为actionList)是一个双向链表。这就是我们使用addHead方法的原因,它将给定值添加到列表的开头。您可以在此处找到所有可用方法

步骤 3. 导入并使用工具栏操作贡献者

在您的路由配置中导入identityToolbarActionContributors,并将其传递给identity路由的静态createRoutes方法,如下所示。

// src/app/app.routes.ts

// 其他导入
import { identityToolbarActionContributors } from './toolbar-action-contributors';

export const APP_ROUTES: Routes = [
  // 其他路由

  {
    path: 'identity',
    loadChildren: () =>
      import('@abp/ng.identity').then(c =>
        c.createRoutes({
          toolbarActionContributors: identityToolbarActionContributors,
        })
      ),
  },

  // 其他路由
];

这样,logUserNames工具栏操作将作为identity包的users页面(UsersComponent)中页面工具栏上的第一个操作添加,并且将由自定义按钮(即ClickMeButtonComponent)触发。请注意,组件投射不仅限于按钮,您可以使用其他UI组件。

如何放置自定义模态框并通过工具栏操作触发它

请查看实体操作扩展文档中的相同主题,并将实体操作替换为工具栏操作。

API

ActionData<R = any>

ActionData是传递给ToolbarAction中所有回调或谓词的参数类型。

它具有以下属性:

  • record 是页面数据,页面上的主记录,通常是实体列表(例如用户列表)。

    {
      text: '点击我!',
      action: data => {
        data.record.forEach(user => {
          console.log(user.userName);
        });
      },
    }
    
  • getInjected 等同于Injector.get。您可以使用它来获取PageToolbarComponent的注入依赖项,包括但不限于其父组件。

    {
      text: '点击我!',
      action: data => {
        const restService = data.getInjected(RestService);
    
        // 在此处使用restService的公共属性和方法
      },
      visible: data => {
        const usersComponent = data.getInjected(UsersComponent);
    
        // 在此处使用usersComponent的公共属性和方法
      },
    }
    

ActionCallback<T, R = any>

ActionCallback是可以作为action参数传递给ToolbarAction的回调函数类型。操作回调接收单个参数ActionData。返回类型可以是任何类型,包括void。以下是简化表示:

type ActionCallback<T, R = any> = (data?: ActionData<T>) => R;

ActionPredicate<T>

ActionPredicate是可以作为visible参数传递给ToolbarAction的谓词函数类型。操作谓词接收单个参数ActionData。返回类型必须为boolean。以下是简化表示:

type ActionPredicate<T> = (data?: ActionData<T>) => boolean;

ToolbarActionOptions<R = any>

ToolbarActionOptions是定义创建工具栏操作时必须传递的必需和可选属性的类型。

其类型定义如下:

type ToolbarActionOptions<R = any> = {
  action: ActionCallback<R>,
  text: string,
  icon?: string,
  permission?: string,
  visible?: ActionPredicate<R>,
};

如您所见,传递actiontext足以创建工具栏操作。以下是每个属性的作用:

  • action 是点击工具栏操作时调用的回调函数。(必需
  • text 是将被本地化的按钮文本。(必需
  • icon 是定义放置在文本前的图标的类。(默认值: ''
  • permission 是权限上下文,用于决定是否应向用户显示此工具栏操作。(默认值: undefined
  • visible 是一个谓词,用于决定页面工具栏是否应具有此操作。(默认值: () => true

您可以在下方找到完整示例。

ToolbarAction<R = any>

ToolbarAction是定义工具栏操作的类。它接收一个ToolbarActionOptions并为属性设置默认值,创建一个可以传递给工具栏贡献者的工具栏操作。

const options: ToolbarActionOptions<IdentityUserDto[]> = {
  action: data => {
    const service = data.getInjected(MyCustomIdentityService);
    const lockedUsers = data.record.filter(user => user.isLockedOut);
    service.unlockAll(lockedUsers);
  },
  text: 'MyProjectName::解锁全部',
  icon: 'fa fa-unlock',
  permission: 'AbpIdentity.Users.Update',
  visible: data => data.record.some(user => user.isLockedOut),
};

const action = new ToolbarAction(options);

它还有两个静态方法来创建其实例:

  • ToolbarAction.create<R = any>(options: ToolbarActionOptions<R>) 用于创建ToolbarAction的实例。

    const action = ToolbarAction.create(options);
    
  • ToolbarAction.createMany<R = any>(options: ToolbarActionOptions<R>[]) 用于使用给定的ToolbarActionOptions数组创建多个ToolbarAction实例。

ToolbarComponentOptions<R = any>

ToolbarComponentOptions是定义创建工具栏组件时必须传递的必需和可选属性的类型。

其类型定义如下:

type ToolbarComponentOptions<R = any> = {
  component: Type<any>,
  action?: ActionCallback<R>,
  permission?: string,
  visible?: ActionPredicate<R>,
};

如您所见,传递actiontext足以创建工具栏操作。以下是每个属性的作用:

  • component 是要被投射的组件的构造函数。(必需
  • action 是一个预定义的回调,您可以在组件中通过EXTENSIONS_ACTION_CALLBACK令牌访问并触发它。(可选
  • permission 是权限上下文,用于决定是否应向用户显示此工具栏操作。(默认值: undefined
  • visible 是一个谓词,用于决定页面工具栏是否应具有此操作。(默认值: () => true

您可以在下方找到完整示例。

ToolbarComponent<R = any>

ToolbarComponent是定义投射自定义组件的工具栏操作的类。它接收一个ToolbarComponentOptions并为属性设置默认值,创建一个可以传递给工具栏贡献者的工具栏操作。

const options: ToolbarComponentOptions<IdentityUserDto[]> = {
  component: UnlockAllButton,
  action: data => {
    const service = data.getInjected(MyCustomIdentityService);
    const lockedUsers = data.record.filter(user => user.isLockedOut);
    service.unlockAll(lockedUsers);
  },
  permission: 'AbpIdentity.Users.Update',
  visible: data => data.record.some(user => user.isLockedOut),
};

const action = new ToolbarComponent(options);

它还有两个静态方法来创建其实例:

  • ToolbarComponent.create<R = any>(options: ToolbarComponentOptions<R>) 用于创建ToolbarComponent的实例。

    const action = ToolbarComponent.create(options);
    
  • ToolbarComponent.createMany<R = any>(options: ToolbarComponentOptions<R>[]) 用于使用给定的ToolbarComponentOptions数组创建多个ToolbarComponent实例。

    const actions = ToolbarComponent.createMany(optionsArray);
    

ToolbarActionList<R = any>

ToolbarActionList是作为第一个参数(名为actionList)传递给每个操作贡献者回调的操作列表。它是一个双向链表。您可以在此处找到所有可用方法

列表中的项将按照链表顺序显示,即从头到尾。如果您想重新排序,只需执行以下操作:

export function reorderUserContributors(
  actionList: ToolbarActionList<IdentityUserDto[]>,
) {
  // 删除"新建用户"按钮
  const newUserActionNode = actionList.dropByValue(
   'AbpIdentity::NewUser',
    (action, text) => action['text'] === text,
  );

  // 将其添加回列表头部
  actionList.addHead(newUserActionNode.value);
}

export const identityEntityActionContributors = {
  [eIdentityComponents.Users]: [
    logUserNamesContributor,
    reorderUserContributors,
  ],
};

ToolbarActionContributorCallback<R = any>

ToolbarActionContributorCallback是您可以作为工具栏操作贡献者回调传递给包静态createRoutes方法的类型。

// exportUsersContributor 应具有 ToolbarActionContributorCallback<IdentityUserDto[]> 类型

export function exportUsersContributor(
  actionList: ToolbarActionList<IdentityUserDto[]>,
) {
  // 在最后一个操作之前添加exportUsers
  actionList.add(exportUsers).byIndex(-1);
}

export const identityEntityActionContributors = {
  [eIdentityComponents.Users]: [exportUsersContributor],
};

另请参阅

在本文档中