4

When my project was using Angular 2 and Angular 4, I was able to use this useful decorator for making a component extend another component:

// Modified from https://medium.com/@ttemplier/angular2-decorators-and-class-inheritance-905921dbd1b7#.s9pf0649f
// by Thierry Templier.

import { Component } from '@angular/core';

export function ComponentExtender(annotation: any): ((target: Function) => void) {
  return function(target: Function): void {
    let parentTarget = Object.getPrototypeOf(target.prototype).constructor;
    let parentAnnotations = Reflect.getMetadata('annotations', parentTarget); // TODO: Doesn't work in Angular 5, returns undefined.
    let parentAnnotation = parentAnnotations[0];

    Object.keys(parentAnnotation).forEach(key => {
      if (parentAnnotation[key]) {
        if (!annotation[key]) {
          annotation[key] = parentAnnotation[key];
        }
      }
    });

    let metadata = new Component(annotation);

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

With this you could create a component class that extended an another component class, using @ComponentExtender instead of @Component, and inherit things from the superclass like the template and style if those things did not need to be changes in the subclass component.

This no longer works now that I've moved my project to Angular 5. The annotations metadata is undefined.

Does this perhaps have something to do with AOT compilation (even though I haven't selected that option)? Can anyone think of a way to revive what this decorator used to do for Angular 2 and Angular 4?

kshetline
  • 12,547
  • 4
  • 37
  • 73
  • Were you able to solve it? I'm running into the same issue – Daniel Arechiga Nov 22 '17 at 22:26
  • My solution, such as it was, was to repeat stuff in the subclass decorators that I didn't have to repeat before. I declared one somewhat detailed bit of animation set-up as a const in the base class so I could re-use it in subclasses without repeating it in full. – kshetline Nov 25 '17 at 00:12

2 Answers2

2

You can get the annotation via target['__annotations__'][0].

There is also '__paramaters__' and '__prop__metadata__', see source of decorators.ts

Note: According to the Angular Blog, the build optimzer now is used by default for production builds, so the annotations will not be available at runtime.

Disclaimer: This is hacky and an implementation detail, so it should never be used in production code!

Markus Ende
  • 830
  • 1
  • 9
  • 21
  • Thanks for the info. If this won't work in production code, I won't use it at all. It's a shame this concept isn't supported for general use -- it was a great way to subclass a component while treating the metadata as part of the inheritance too. – kshetline Nov 16 '17 at 00:00
  • I'm not saying that it wont work. You can disable the build optimizer with option `--build-optimizer=false`. But the caveats are: * well, the build will not be optimized... * the whole implementation is based on an implementation detail af Angular and thus prone to arbitrary changes. On the other hand, you already walked down this road by depending on Reflect.getMetadata. – Markus Ende Nov 18 '17 at 12:28
  • If this answers your question, would you mind marking it as answer? – Markus Ende Nov 18 '17 at 12:34
  • @MarkusEnde getting parent annotations is working with `target['__annotations__'][0]`, but how to set metadata for the current component. This code doesn't work any more `let metadata = new Component(annotation); Reflect.defineMetadata('annotations', [ metadata ], target);` Thank you. – Nikola Yankov Nov 25 '17 at 08:28
  • @NikolaYankov Reflect is not used anymore. The annotation metadata is stored in the ``__annotations__`` property. So, to add/change it you just have to store the metadata in that property, i guess (did not try). Try this: ``target['__annotations__'][0] = metadata;`` – Markus Ende Dec 01 '17 at 09:22
2

You can save component's annotation in separate variable and export it together with your component, so you don't need to retrieve it from component's metadata. here is small example, hope it would help you.

https://stackblitz.com/edit/angular-bxbpwr?embed=1&file=app/asignAnnotations.ts

Treeskar
  • 566
  • 5
  • 6