Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular: Creating plugins for 3rd party packages (libraries)

I created Angular Library (ngx-wig) and I would like to provide an ability to extend its functionality by using plugins.

What would be the best place to declare plugin in Angular? (may be something like myLibModule.forRoot(..)) and what type of instance should be plugin itself?

I solved same issue for AngularJs just by adding module for each plugin in which I register plugin by using configProvider of main module. Don't really like this solution because plugin registers itself, but it should be responsibility of applications where library is used.

UPDATE: related issue is opened on github here.

like image 686
Stepan Suvorov Avatar asked Jun 20 '17 13:06

Stepan Suvorov


People also ask

What is difference between package and library in angular?

Both libraries and applications are modules a the end of the day. Their main difference is how they are built and deployed. A library can be packaged and published for easier reuse across projects. An application includes browser specific files for deploying and running in the browser.


1 Answers

I think you can provide users to use component as a plug-in. This component has to extends you abstract base plugin component.

For example clear-styles plugin could look like

@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 plugin

@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();
  }
}

where Ng2WigPluginComponent is abstract base class provided by your library:

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

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

So users can easily use declared in base class properties.

To register such plugins we can use mentioned by you forRoot method. For that you need to

1) configure you library module like follows:

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},
      ]
    };
  }
}

where

  • NG_WIG_CUSTOM_BUTTONS is your global library token to recognize provided plugins inside library

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 is angular global token to be able to load plugins dynamically

2) Declare NwClearStylesButtonComponent in declarations array of your AppModule module

3) Pass it to the Ng2WigModule.forRoot method

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

And then main you task will be to dynamically generate your component by using ComponentFactoryResolver and ViewContainerRef (see ng2wig-plugin.directive.ts in plunker below)

Plunker Example

like image 144
yurzui Avatar answered Sep 20 '22 22:09

yurzui