programing

기본 클래스 장식기로 구성 요소 장식기 확장

instargram 2023. 7. 6. 21:46
반응형

기본 클래스 장식기로 구성 요소 장식기 확장

모든 구성요소에 대해 반복하는 몇 가지 구성요소 장식자 선언이 있습니다. 예:

@Component({
    moduleId: module.id,
    directives: [BootstrapInputDirective]
})

이러한 선언을 모든 구성 요소에 적용하려면 어떻게 해야 합니까?이 데코레이터로 기본 클래스를 만들고 다른 클래스를 확장하려고 했는데 파생 클래스에는 기본 클래스 장식이 적용되지 않는 것 같습니다.

@Component장식가입니다.즉, 리플렉트 메타데이터 라이브러리를 활용하여 일부 메타데이터 데이터를 추가하여 적용되는 클래스를 처리합니다.Angular2는 상위 클래스에서 메타데이터를 찾지 않습니다.이러한 이유로 부모 클래스에서는 장식자를 사용할 수 없습니다.

에 대해서는BootstrapInputDirective지시, 당신은 그것을 플랫폼 1로 정의할 수 있습니다.이 방법을 사용하면 매번 파일에 포함할 필요가 없습니다.directives사용자 구성 요소의 속성입니다.

다음은 샘플입니다.

(...)
import {PLATFORM_DIRECTIVES} from 'angular2/core';

bootstrap(AppComponent, [
  provide(PLATFORM_DIRECTIVES, {useValue: [BootstrapInputDirective], multi:true})
]);

편집

예, 당신은 이것을 구현하기 위해 당신만의 장식가를 만들 수 있습니다.다음은 샘플입니다.

export function CustomComponent(annotation: any) {
  return function (target: Function) {
    var parentTarget = annotation.parent;
    delete annotation.parent;
    var parentAnnotations = Reflect.getMetadata('annotations', parentTarget);

    var parentAnnotation = parentAnnotations[0];
    Object.keys(parentAnnotation).forEach(key => {
      if (isPresent(parentAnnotation[key])) {
        annotation[key] = parentAnnotation[key];
      }
    });
    var metadata = new ComponentMetadata(annotation);

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

CustomComponent데코레이터는 다음과 같이 사용됩니다.

@Component({
  template: `
    <div>Test</div>
  `
})
export class AbstractComponent {
}

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

장식자 내에서 이 부모 클래스를 찾을 수 있기 때문에 부모 클래스를 장식자의 입력으로 제공해야 합니다.이 클래스의 프로토타입만 적용되고 메타데이터는 반사 메타데이터에 의해 관련 프로토타입에 적용되지 않습니다.

편집2

Nitzam의 답변 덕분에 개선 사항이 있습니다.

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])) {
        annotation[key] = parentAnnotation[key];
      }
    });
    var metadata = new ComponentMetadata(annotation);

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

의 필요성은 없습니다.parent사용자 지정 장식기의 상위 클래스를 참조하는 속성입니다.

다음 플런크r을 참조하십시오. https://plnkr.co/edit/ks1iK41sIBFlYDb4aTHG?p=preview

다음 질문을 참조하십시오.

현재 기능을 찾고 있는 경우:

function isPresent(obj: any): boolean { return obj !== undefined && obj !== null; }

Angular의 최신 릴리스 이후, ComponentMetadata 클래스는 여기에 있는 소수의 구성원들이 지적한 것처럼 사용할 수 없습니다.

사용자 지정 구성 요소를 구현하여 작동시키는 방법은 다음과 같습니다.

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

      let parentAnnotation = parentAnnotations[0];

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

도움이 되길 바랍니다!

EDIT: 이전 코드 청크는 작동하더라도 확장 클래스의 원래 메타데이터를 재정의합니다.기본 클래스를 수정하지 않고 여러 상속 및 재정의를 가질 수 있는 향상된 버전의 아래를 확인하십시오.

export function ExtendComponent(annotation: any) {
  return function (target: Function) {
    let currentTarget = target.prototype.constructor;
    let parentTarget = Object.getPrototypeOf(target.prototype).constructor;
    let parentAnnotations = Reflect.getOwnMetadata('annotations', parentTarget);

    Reflect.defineMetadata('annotations', [Object.create(parentAnnotations[0])], currentTarget);
    let currentAnnotations = Reflect.getOwnMetadata('annotations', currentTarget);

    Object.keys(annotation).forEach(key => {
        currentAnnotations[0][key] = annotation[key];
    });
};

}

업데이트된 해결책을 찾고 있는 사람이 있다면 티에리 템플러의 대답은 거의 완벽합니다.그것을 제외하고는ComponentMetadata더 이상 사용되지 않습니다.용사를 합니다.Component대신 나를 위해 일했습니다.

커스텀 풀커텀데이터레코스.CustomDecorator.ts파일은 다음과 같습니다.

import 'zone.js';
import 'reflect-metadata';
import { Component } from '@angular/core';
import { isPresent } from "@angular/platform-browser/src/facade/lang";

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])) {
        // verify is annotation typeof function
        if(typeof annotation[key] === 'function'){
          annotation[key] = annotation[key].call(this, parentAnnotation[key]);
        }else if(
          // force override in annotation base
          !isPresent(annotation[key])
        ){
          annotation[key] = parentAnnotation[key];
        }
      }
    });

    var metadata = new Component(annotation);

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

다음 새 인 그런다새구요가로 가져옵니다.sub-component.component.ts 합니다.@CustomComponent@Component다음과 같이:

import { CustomComponent } from './CustomDecorator';
import { AbstractComponent } from 'path/to/file';

...

@CustomComponent({
  selector: 'subcomponent'
})
export class SubComponent extends AbstractComponent {

  constructor() {
    super();
  }

  // Add new logic here!
}

Tierry Templier의 솔루션은 Angular 9+ check (Ivy, AOT) https://github.com/angular/angular/issues/31495 에서 제대로 작동하지 않을 것입니다.

다음과 같은 부트스트랩 기능으로 글로벌 서비스를 제공할 수 있습니다.

bootstrap(AppComponent, [HTTP_PROVIDERS, provide(SharedService, {useValue: sharedService})]);

여기서 sharedService는 가져온 서비스입니다.

언급URL : https://stackoverflow.com/questions/36837421/extending-component-decorator-with-base-class-decorator

반응형