【问题标题】:Angular: Creating plugins for 3rd party packages (libraries)Angular:为第 3 方包(库)创建插件
【发布时间】:2017-11-23 01:59:59
【问题描述】:

我创建了 Angular 库 (ngx-wig),我想提供一种通过使用插件来扩展其功能的能力。

在 Angular 中声明插件的最佳位置是什么? (可能类似于myLibModule.forRoot(..)),什么类型的实例应该是插件本身?

solved same issue for AngularJs 只是通过使用主模块的configProvider 为我注册插件的每个插件添加模块。不是很喜欢这个解决方案,因为插件自己注册,但它应该是使用库的应用程序的责任。

更新:相关问题已在 github here 上打开。

【问题讨论】:

  • 好像有点不清楚。您想提供类似 API 的服务吗?
  • 这取决于插件。其中一些可以使用新按钮扩展工具栏
  • 好问题。我在模块化音乐制作系统中遇到了类似的问题,解决方案是每个插件的“表面部分”必须遵守定义 plugin assembly 的接口,例如关于插件的 key-metadata,例如名称、key-classes 等,然后插件本身的内部部分通过放置在方法上的 decorator-hooks 工作(例如@nodeDidActivate@nodeStateDidChange 等)。这个系统使用 React 作为它的 UI 层,但它在 ng 中应该没有什么不同。

标签: javascript angular angular-module angular-library


【解决方案1】:

我认为你可以提供用户使用组件作为插件。该组件必须扩展您的抽象基础插件组件。

例如clear-styles 插件可能看起来像

@Component({
  selector: `nw-clear-styles-button`,
  template: `
    <button (click)="clearStyles($event)" 
        [disabled]="editMode || disabled" 
        class="nw-button clear-styles" title="Clear Styles">
      Clear Styles
    </button>`
})
export class NwClearStylesButtonComponent extends Ng2WigPluginComponent {
  constructor() {
    super();
  }

  clearStyles() {
    const div = document.createElement('div');
    div.innerHTML = this.content;
    this.contentChange.emit(div.textContent);
  }
}

format插件

@Component({
  selector: `nw-formats-button`,
  template: `
    <select class="nw-select"
            [(ngModel)]="format"
            (ngModelChange)="execCommand('formatblock', format.value)"
            [disabled]="editMode || disabled">
      <option *ngFor="let format of formats" [ngValue]="format">{{ format.name }}</option>
    </select>
  `
})
export class NwFormatButtonComponent extends Ng2WigPluginComponent {
  formats = [
    {name: 'Normal text', value: '<p>'},
    {name: 'Header 1', value: '<h1>'},
    {name: 'Header 2', value: '<h2>'},
    {name: 'Header 3', value: '<h3>'}
  ];

  format = this.formats[0];

  constructor() {
    super();
  }
}

Ng2WigPluginComponent 是您的库提供的抽象基类:

export abstract class Ng2WigPluginComponent {
  execCommand: Function;
  editMode: boolean;
  content: string;

  editModelChange: EventEmitter<boolean> = new EventEmitter();
  contentChange: EventEmitter<string> = new EventEmitter();
}

因此用户可以轻松地使用在基类中声明的属性。

要注册此类插件,我们可以使用您提到的forRoot 方法。为此,您需要

1)如下配置你的库模块:

ng2wig.module.ts

@NgModule({
 ...
})
export class Ng2WigModule {
  static forRoot(entryComponents: CustomButton[]) {
    return {
      ngModule: Ng2WigModule,
      providers: [
        Ng2WigToolbarService,
        {provide: NG_WIG_CUSTOM_BUTTONS, useValue: entryComponents},
        {provide: ANALYZE_FOR_ENTRY_COMPONENTS, multi: true, useValue: entryComponents},
      ]
    };
  }
}

在哪里

  • NG_WIG_CUSTOM_BUTTONS 是您的全局库令牌,用于识别库中提供的插件

ng2wig-toolbar.service.ts

@Injectable()
export class Ng2WigToolbarService {
  constructor(@Optional() @Inject(NG_WIG_CUSTOM_BUTTONS) customButtons: CustomButton[]) {
    if (customButtons) {
      customButtons.forEach(plugin => this.addCustomButton(plugin.pluginName, plugin.component));
    }
  }
  • ANALYZE_FOR_ENTRY_COMPONENTS 是 Angular 全局令牌,能够动态加载插件

2)AppModule 模块的声明数组中声明 NwClearStylesButtonComponent

3) 将其传递给Ng2WigModule.forRoot 方法

Ng2WigModule.forRoot([
   { pluginName: 'clear-styles', component: NwClearStylesButtonComponent },
   { pluginName: 'format', component: NwFormatButtonComponent }
]) 

然后您的主要任务是使用ComponentFactoryResolverViewContainerRef 动态生成组件(请参阅下面的plunker 中的ng2wig-plugin.directive.ts

Plunker Example

【讨论】:

  • 谢谢您,您的回答很棒!我之前没有提供赏金来激励其他人分享他们的答案
猜你喜欢
  • 2012-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-29
  • 2019-08-05
  • 2019-02-13
  • 2019-05-27
相关资源
最近更新 更多