Override/extend third-party component's templa

2020-07-03 04:31发布

问题:

I am currently importing a third party component. For my use case I need to override that specific component template.

Since this is a third party component, and imported via npm package, I don't want to change the component so I don't have to update it everytime the package is updated.

Is there any way to overwrite the template of another component?

I know you can use <ng-content> if you want to inject some element. But here is not viable.

The html is something like this:

<third-party-component [items]="items" [example]="example">

The controller is something like this:

import {THIRD_PARTY_DIRECTIVES} from 'ng2-select/ng2-select';

@Component({
  selector: 'example-component',
  directives: [THIRD_PARTY_DIRECTIVES]
})
export class Example {

  private items: Array<string> = [
    'whatever', 'whatever2', 'whatever3'
  ];
}

Is there any way I can specify the template I want for <third-party-component> without editing that specific component declaration? Or even extend it only ?

回答1:

After playing around with it. A simple extend will work for my use case.

Basically I created a class that extends the thirdPartyClass.

What happens here is that I am overwriting the template for the thirdPartyClass by creating my own selector and importing only the class.

Something like this:

import {component} from 'angular2/core';
import {thirdPartyClass} from 'example/example';

@Component({
  selector: 'my-selector',
  template: '<div>my template</div>'
})

export class MyOwnComponent extends thirdPartyClass {
  constructor() {
     super()
  }
}

Notes:

  • If you are using this method, don't forget to import any pipes that are used in the thirdPartyClass template.
  • If the functionality is updated in the thirdPartyClass that depends upon the template, you'll need to update by hand.
  • I prefered this solution to refering to the ReflectMetaData because its a simple extend instead of accessing the annotations and force changing it.


回答2:

You can use Reflect to change metadata of component. Here's the super simple example:

import {Component} from 'angular2/core'

@Component({
  selector: 'thing',
  template: `Hi!`,
})
export class Thing {}

annotations = Reflect.getMetadata('annotations', Thing);
for (let i = 0; i < annotations.length; i += 1) {
  if (annotations[i].constructor.name === 'ComponentMetadata') {
    annotations[i].template = 'Ho!';
    break;
  }
}

@Component({
  selector: 'my-app',
  directives: [Thing],
  template: `<thing></thing>`,
})
export class App {}

Just make sure you update template before injecting it into parent component. Also check which metadata you need to access, might be DirectiveMetadata in your case.