2

I'm creating Web apps using Angular2 with TypeScript. When I created some CustomElements (it's mean Components and Directives, Validators), I found that I wrote directives: [...] code to every Components in order to import CustomElements like the below code.

// my_comopnent_1.comopnent.ts
@Component({
  selector: 'my-component-1',
  directives: [
    ROUTER_DIRECTIVES,
    MyDirective1,
    MyDirective2,
    MyValidator1,
    MyValidator2,
    ...
  ],
})

// my_comopnent_2.comopnent.ts
@Component({
  selector: 'my-component-2',
  directives: [
    ROUTER_DIRECTIVES,
    MyDirective1,
    MyDirective2,
    MyValidator1,
    MyValidator2,
    ...
  ],
})

// my_comopnent_3.comopnent.ts
@Component({
  selector: 'my-component-3',
  directives: [
    ROUTER_DIRECTIVES,
    MyDirective1,
    MyDirective2,
    MyValidator1,
    MyValidator2,
    ...
  ],
})

Does the way exist how some CustomComopnents can be imported without repeatedly writing directives: [...], like event bubbling or prototype chain? My ideal is that when I write the code to parent Components, its child Components includes them parent's imported CustomElements.

// parent.component.ts
@Component({
  selector: 'parent',
  directives: [MyDirective1, ...],
})

// child.component.ts
@Component({
  selector: 'child',
  template: `
    // It's possible to use MyDirective because of importing by ParentComponent.
    <div my-directive></div>
  `,
})
Yohsuke Inoda
  • 521
  • 1
  • 6
  • 20

2 Answers2

0

You could define them as platform directives this way:

export const GENERAL_DIRECTIVES: any[] = [
  ROUTER_DIRECTIVES,
  MyDirective1,
  MyDirective2,
  MyValidator1,
  MyValidator2,
  (...)
];

bootstrap(App, [
  provide(PLATFORM_DIRECTIVES, {useValue: [GENERAL_DIRECTIVES], multi: true})
]);

This way you don't need to import them each time.

See this question for more details:

The main drawback of this approach is that they are global to all components in your application. Another approach consists of creating a custom decorator that extends the component metadata (directives attribute) to add these components / directives. This way you can finely control where you want to have these directives automatically specified.

This mechanism can rely on inheritence, i.e. an abtract root component.

Here is a sample:

export function CustomComponent(annotation: any) {
  return function (target: Function) {
    var parentTarget = Object.getPrototypeOf(target.prototype).constructor;
    var parentAnnotations = Reflect.getMetadata('annotations', parentTarget);

    var parentAnnotation = parentAnnotations[0];
    Object.keys(parentAnnotation).forEach(key => {
      if (isPresent(parentAnnotation[key])) {
        if (key === 'directives') {
          // merge directives attributes
          let parentDirectives = parentAnnotation[key] || [];
          let currentDirectives = annotation[key] || [];
          currentDirectives.concat(parentDirectives);
        } else {
          annotation[key] = parentAnnotation[key];
        }
      }
    });
    var metadata = new ComponentMetadata(annotation);

    Reflect.defineMetadata('annotations', [ metadata ], target);
  }
}

and use it:

@Component({
  directives: [
    ROUTER_DIRECTIVES,
    MyDirective1,
    MyDirective2,
    MyValidator1,
    MyValidator2,
    (...)
  ]
})
export class AbstractComponent {
}

@CustomComponent({
  selector: 'sub',
  template: `
    (...)
  `
})
export class SubComponent extends AbstractComponent {
}

@Component({
  selector: 'app',
  template: `
    <sub></sub>
  `,
  directives [ SubComponent ]
})
export class App {
}

See this question for more details:

Community
  • 1
  • 1
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
0

You can globally provide directives and piples like

bootstrap(AppComponent, [
    provide(PLATFORM_DIRECTIVES, 
        {useValue: [
            ROUTER_DIRECTIVES, 
            MyDirective1, 
            MyDirective2, 
            MyValidator1, 
            MyValidator2
         ], multi: true}),

    provide(PLATFORM_PIPES, 
         {useValue: [RainbowizePipe], multi:true})

]);

The arrays of directives and pipes you pass are flattened automatically by Angulars DI this allows to pass arbitrary nested arrays. For example to pack the directives of a module into an array and then finally pass an array that contains the arrays of all modules to provide().

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567