Написать собственную ангулярную2 аннотацию

Сегодня я пытаюсь создать собственную аннотацию, чтобы использовать ее в проекте Angular2. Эта аннотация должна добавить класс тела для конкретного компонента.

Итак, я искал исходный код Angular, но так сложно увидеть, где и как была создана аннотация.

На данный момент я попробовал это:

export function BodyClass(classes: any): ClassDecorator{
    classes = classes || {};

    if(typeof classes === 'string'){
        classes = {classes};
    }

    return function changeBodyClass(){
        console.log('ici');
    }
}

И мой компонент:

import {Component} from "angular2/core";
import {RouterOutlet} from "angular2/router";
import {BodyClass} from "../core/annotations/body_class";

@Component({
    selector: 'my-component',
    template: `
        <router-outlet></router-outlet>
    `,
})
@BodyClass('test')
export class MyComponent{
}

Мой консольный журнал в аннотации был запущен правильно, но я хочу использовать класс "DOM" из angular2/src/platform/dom/dom_adapter чтобы добавить мои классы, но DOM в неопределенном, когда на консоли журнала его (не нужно создавать его экземпляр).

Тем не менее, класс DOM хорошо работает непосредственно в моем компоненте. Я добавляю классы в функцию ngOnInit и удаляю их в ngOnDestroy.

Но я хочу, чтобы такое поведение было во многих компонентах, и я думаю, что новая аннотация - лучший способ.

Может быть, у вас есть идея для этого лучше? Или работать с классом DOM над аннотацией?

Спасибо!

1 ответ

Это не так очевидно, поскольку вы хотите работать на уровне экземпляра компонента, а не на уровне класса компонента. Поэтому вам нужно обернуть соответствующую функцию конструктора.

export function MyComponentDecorator(value: string) {
  return function (target: Function) {
    var original = target;

    function construct(constructor, args) {
      var c : any = function () {
        // Call the target constructor
        var ret = constructor.apply(this, args);

        // Add your additional processing

        return ret;
      }
      c.prototype = constructor.prototype;
      return new c();
    }

    // The new constructor
    // Don't forget to add a name at this function
    // since Angular requires it for the template compilation
    var f : any = function WrappedComponent(...args) {
      return construct(original, args);
    }

    f.prototype = original.prototype;

    return f;
  }
}

На этом этапе вы оборачиваете экземпляр компонента, но теряете метаданные компонента. Вам нужно скопировать их вручную:

f.prototype = original.prototype;

var annotations = Reflect.getMetadata('annotations', original));
Reflect.defineMetadata('annotations', annotations, f);

var properties = Reflect.getMetadata('propMetadata', original));
Reflect.defineMetadata('propMetadata', properties, f);

return f;

Чтобы использовать этот декоратор просто добавьте его до или после @Component один:

@MyComponentDecorator()
@Component({
  selector: 'sub', 
  template: `
    <div (click)="showMessage()">Test</div>
  `
})
export class SubComponent {
  (...)
}

Вы можете заметить, что этот декоратор копирует метаданные только на уровне компонентов, а не другие, такие как свойства (с @Input)...

Смотрите этот план: https://plnkr.co/edit/PrAvliIpWTNFAtH33bHA?p=preview.

Другие вопросы по тегам