source

컴포넌트를 확장/상속하는 방법

myloves 2023. 3. 4. 15:27

컴포넌트를 확장/상속하는 방법

기본 컴포넌트가 변경될 수 있기 때문에 Angular 2에 이미 배치된 컴포넌트 중 일부를 완전히 다시 작성할 필요 없이 확장 버전을 만들고 싶습니다.

이 간단한 예제를 작성했습니다.내 질문을 더 잘 설명하기 위해서입니다.

요소 포함app/base-panel.component.ts:

import {Component, Input} from 'angular2/core';

@Component({
    selector: 'base-panel',
    template: '<div class="panel" [style.background-color]="color" (click)="onClick($event)">{{content}}</div>',
    styles: [`
    .panel{
    padding: 50px;
  }
  `]
})
export class BasePanelComponent { 

  @Input() content: string;

  color: string = "red";

  onClick(event){
    console.log("Click color: " + this.color);
  }
}

파생 를 들어, 기본 를 들어, 에서는 기본 컴포넌트의 을 변경할 수 있습니다.app/my-panel.component.ts:

import {Component} from 'angular2/core';
import {BasePanelComponent} from './base-panel.component'

@Component({
    selector: 'my-panel',
    template: '<div class="panel" [style.background-color]="color" (click)="onClick($event)">{{content}}</div>',
    styles: [`
    .panel{
    padding: 50px;
  }
  `]
})
export class MyPanelComponent extends BasePanelComponent{

  constructor() {
    super();
    this.color = "blue";
  }
}

Plunker의 완전한 작업 예

주의: 이 예는 분명 단순하며 그렇지 않으면 상속을 사용할 필요가 없습니다.그러나 이는 실제 문제를 설명하기 위한 것입니다.

컴포넌트 에서 알 수app/my-panel.component.ts 계승된 은 「이것」, 「은 '이것'입니다class BasePanelComponent ,는@Component 부분만 것이 . 단순히 변경된 부분뿐만 아니라selector: 'my-panel'.

"Angular2" 상속하여 "Angular2"를 상속할 수 있는 ?class예: '마킹/표기@Component

편집 1 - 기능 요청

GitHub의 프로젝트에 추가된 기능 요청 angular2: 확장/상속 angular2 컴포넌트 주석 #7968

편집 2 - 마감된 요청

이 때문에 데코레이터의 결합 방법을 짧게 알 수 없는 요청이 종료되었습니다.선택의 여지가 없는 거죠그래서 내 의견은 그 이슈에 인용되었다.

대체 솔루션:

티에리 템플리어의 이 답변은 문제를 해결할 수 있는 대안이다.

Tierry Templier와 몇 가지 질문을 한 후, 저는 이 질문에서 언급된 상속 제한의 대안으로 제 기대에 부합하는 다음과 같은 작업 예를 들었습니다.

1 - 커스텀 데코레이터 작성:

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);
  }
}

2 - 기본 컴포넌트(@Component decorator):

@Component({
  // create seletor base for test override property
  selector: 'master',
  template: `
    <div>Test</div>
  `
})
export class AbstractComponent {

}

3 - @Custom Component 데코레이터를 사용하는 서브 컴포넌트:

@CustomComponent({
  // override property annotation
  //selector: 'sub',
  selector: (parentSelector) => { return parentSelector + 'sub'}
})
export class SubComponent extends AbstractComponent {
  constructor() {
  }
}

플렁커에 완전한 예를 제시합니다.

Angular의 컴포넌트 상속 시스템에 관한 몇 가지 주요 제한과 기능에 대해 설명합니다.

컴포넌트는 클래스 로직만 상속합니다.

  • @Component 데코레이터의 모든 메타데이터는 상속되지 않습니다.
  • 컴포넌트 @Input 속성 및 @Output 속성이 상속됩니다.
  • 구성 요소 수명 주기가 상속되지 않습니다.

이러한 기능은 매우 중요하므로 각각 개별적으로 살펴보겠습니다.

구성 요소는 클래스 로직만 상속합니다.

구성요소를 상속하면 내부의 모든 논리가 동일하게 상속됩니다.개인 구성원은 구현된 클래스에서만 액세스할 수 있으므로 공용 구성원만 상속됩니다.

@Component 데코레이터의 모든 메타데이터는 상속되지 않습니다.

메타 데이터가 상속되지 않는다는 사실은 처음에는 직관에 반하는 것처럼 보일 수 있지만, 이 점을 생각해 보면 완벽하게 이해가 됩니다.컴포넌트 예(componentA)에서 상속하는 경우 상속되는 컴포넌트A의 셀렉터가 상속되는 클래스인 컴포넌트B의 셀렉터를 대체하지 않도록 할 수 있습니다.template/templateUrl뿐만 아니라 style/StyleUrl도 마찬가지입니다.

컴포넌트 @Input 및 @Output 속성이 상속됩니다.

이것은 또한 Inheritance in Angular 컴포넌트에 대해 매우 좋아하는 기능입니다.간단히 말하면 커스텀 @Input 속성 및 @Output 속성이 있을 때마다 이러한 속성이 상속됩니다.

구성 요소 수명 주기가 상속되지 않음

이 부분은 특히 OOP 원칙을 광범위하게 적용하지 않은 사람들에게는 그다지 명확하지 않은 부분입니다.예를 들어 OnInit와 같은 Angular의 많은 라이프 사이클 훅 중 하나를 구현하는 ComponentA가 있다고 가정합니다.Component B를 만들고 Component A를 상속하는 경우 Component B의 OnInit 라이프 사이클이 있어도 Component A의 OnInit 라이프 사이클이 명시적으로 호출될 때까지 실행되지 않습니다.

슈퍼/베이스 컴포넌트 메서드 호출

ComponentA에서 ngOnInit() 메서드를 기동하려면 super 키워드를 사용하여 필요한 메서드를 호출해야 합니다.이 경우 ngOnInit입니다.super 키워드는 상속되는 컴포넌트의 인스턴스를 나타냅니다.이 경우 ComponentA가 됩니다.

Angular 2 버전 2.3은 최근에 출시되었으며 기본 구성 요소 상속이 포함되어 있습니다.템플릿 및 스타일을 제외하고 원하는 모든 항목을 상속 및 재정의할 수 있습니다.참고 자료:

갱신하다

컴포넌트 상속은 2.3.0-rc.0 이후 지원됩니다.

원래의

까지의 나에게 가장 편리한 것은 입니다.*html&*.css하고, 「파일」을 사용해 지정합니다.templateUrl ★★★★★★★★★★★★★★★★★」styleUrls재사용이 용이합니다.

@Component {
    selector: 'my-panel',
    templateUrl: 'app/components/panel.html', 
    styleUrls: ['app/components/panel.css']
}
export class MyPanelComponent extends BasePanelComponent

이제 TypeScript 2.2클래스 식에서 Mixins를 지원하므로 컴포넌트에서 Mixins를 훨씬 더 잘 표현할 수 있게 되었습니다.각진 2.3(토론) 이후 구성요소 상속 또는 여기서 설명하는 사용자 정의 장식기를 사용할 수도 있습니다.단, Mixin에는 컴포넌트 전체의 동작을 재사용하기 위한 몇 가지 특성이 있다고 생각합니다.

  • 믹스인은 보다 유연하게 구성되며, 기존 컴포넌트에 믹스인을 조합하거나 믹스인을 조합하여 새로운 컴포넌트를 형성할 수 있습니다.
  • 클래스 상속 계층에 대한 명백한 선형화로 인해 믹스인 구성은 이해하기 쉬운 상태로 유지됩니다.
  • 컴포넌트 상속(토론)을 방해하는 데코레이터와 주석을 사용하면 문제를 쉽게 방지할 수 있습니다.

위의 TypeScript 2.2 발표를 읽고 Mixins의 구조를 이해할 것을 강력히 권장합니다.각진 GitHub 문제에 대한 링크된 논의는 추가 세부사항을 제공합니다.

다음과 같은 유형이 필요합니다.

export type Constructor<T> = new (...args: any[]) => T;

export class MixinRoot {
}

.Destroyable가 에서 하는 데 이 되는 ngOnDestroy

export function Destroyable<T extends Constructor<{}>>(Base: T) {
  return class Mixin extends Base implements OnDestroy {
    private readonly subscriptions: Subscription[] = [];

    protected registerSubscription(sub: Subscription) {
      this.subscriptions.push(sub);
    }

    public ngOnDestroy() {
      this.subscriptions.forEach(x => x.unsubscribe());
      this.subscriptions.length = 0; // release memory
    }
  };
}

DestroyableComponent 자신의

export class DashboardComponent extends Destroyable(MixinRoot) 
    implements OnInit, OnDestroy { ... }

:MixinRoot한 것은, 꼭 한 것은, 라고 하는 입니다.extend을 쉽게 할 수 . 예를 여러믹스인을 쉽게 할 수 있습니다.A extends B(C(D))를 들어 상속 .예를 들어 상속 계층을 효과적으로 구성하고 있습니다.A -> B -> C -> D.

예를 들어 기존 클래스에서 Mixin을 작성하려는 경우 다음과 같이 Mixin을 적용할 수 있습니다.

const MyClassWithMixin = MyMixin(MyClass);

하지만 첫 번째 방법이 가장 효과적이라는 것을 알게 되었습니다.Components ★★★★★★★★★★★★★★★★★」Directives, 이것들은 「이것들」로 가 있기 @Component ★★★★★★★★★★★★★★★★★」@Directive★★★★★★★★★★★★★★★★★★.

되지 않은 있으며,는 (루트를 선택한 타이프스크립트를에 Angular 2를 상속을 할 수 .상속을 사용할 수 있습니다.class MyClass extends OtherClass { ... }컴포넌트 상속을 위해서는 https://github.com/angular/angular/issues에 접속하여 기능 요청을 제출하여 Angular 2 프로젝트에 참여할 것을 권장합니다.

CDK 라이브러리와 재료 라이브러리를 읽어보면 컴포넌트 자체에는 그다지 많지 않지만 상속을 사용하고 있습니다.콘텐츠 투사에는 IMO가 포함됩니다.이 링크에는 "이 설계의 주요 문제"가 기재되어 있습니다.https://blog.angular-university.io/angular-ng-content/

이것이 당신의 질문에 답하지 못한다는 것을 알지만, 저는 정말로 컴포넌트를 상속/확장하는 것을 피해야 한다고 생각합니다.제 이유는 이렇습니다.

둘 이상의 컴포넌트에 의해 확장된 추상 클래스에 공유 로직이 포함되어 있는 경우, 서비스를 사용하거나 두 컴포넌트 간에 공유할 수 있는 새로운 타입 스크립트클래스를 만듭니다.

추상수업이...에는 공유 변수 또는 onClicketc 함수가 포함되어 있습니다.그러면 두 확장 컴포넌트 뷰의 html 사이에 중복이 발생합니다.이는 잘못된 관행으로 공유 html을 컴포넌트로 분할해야 합니다.이들 컴포넌트(부품)는 2개의 컴포넌트 간에 공유할 수 있습니다.

컴포넌트에 대한 추상 클래스를 수강해야 하는 다른 이유가 누락되어 있습니까?

최근에 본 예로는 AutoUnsubscribe를 확장하는 컴포넌트가 있습니다.

import { Subscription } from 'rxjs';
import { OnDestroy } from '@angular/core';
export abstract class AutoUnsubscribeComponent implements OnDestroy {
  protected infiniteSubscriptions: Array<Subscription>;

  constructor() {
    this.infiniteSubscriptions = [];
  }

  ngOnDestroy() {
    this.infiniteSubscriptions.forEach((subscription) => {
      subscription.unsubscribe();
    });
  }
}

은 큰 베이스 「Code Base」가 존재했기 때문입니다.infiniteSubscriptions.push()10월 10일Import 및 확장도 가능AutoUnsubscribe를 더하는 보다 더 를 사용합니다.mySubscription.unsubscribe() ngOnDestroy()컴포넌트 자체의 메서드(어차피 추가 로직이 필요).

업데이트된 솔루션을 찾는 사람이 있다면 페르난도의 답변은 거의 완벽합니다., 그 이외에는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);
  }
}

다음 새 컴포넌트로 . Importsub-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!
}

선택기를 새 이름으로 재정의해야 하는 경우에만 구성 요소를 형식 스크립트 클래스 상속과 동일하게 확장할 수 있습니다.부모 컴포넌트의 모든 Input() 및 Output() 속성은 정상적으로 동작합니다.

갱신하다

@컴포넌트는 데코레이터입니다.

데코레이터는 객체가 아닌 클래스 선언 중에 적용됩니다.

기본적으로 데코레이터는 클래스 개체에 일부 메타데이터를 추가하고 상속을 통해 액세스할 수 없습니다.

만약 당신이 데코레이터 상속을 얻고 싶다면, 나는 커스텀 데코레이터를 쓰는 것을 제안합니다.예를 들면 다음과 같습니다.

export function CustomComponent(annotation: any) {
    return function (target: Function) {
    var parentTarget = Object.getPrototypeOf(target.prototype).constructor;

    var parentAnnotations = Reflect.getMetadata('annotations', parentTarget);
    var parentParamTypes = Reflect.getMetadata('design:paramtypes', parentTarget);
    var parentPropMetadata = Reflect.getMetadata('propMetadata', parentTarget);
    var parentParameters = Reflect.getMetadata('parameters', parentTarget);

    var parentAnnotation = parentAnnotations[0];

    Object.keys(parentAnnotation).forEach(key => {
    if (isPresent(parentAnnotation[key])) {
        if (!isPresent(annotation[key])) {
        annotation[key] = parentAnnotation[key];
        }
    }
    });
    // Same for the other metadata
    var metadata = new ComponentMetadata(annotation);

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

참조처: https://medium.com/ @ttemplier / param2-supervators-and-class-dbd1b7

@Input, @Output, @ViewChild 등을 상속할 수 있습니다.샘플을 보세요.

@Component({
    template: ''
})
export class BaseComponent {
    @Input() someInput: any = 'something';

    @Output() someOutput: EventEmitter<void> = new EventEmitter<void>();

}

@Component({
    selector: 'app-derived',
    template: '<div (click)="someOutput.emit()">{{someInput}}</div>',
    providers: [
        { provide: BaseComponent, useExisting: DerivedComponent }
    ]
})
export class DerivedComponent {

}

상속 사용, 자식 클래스의 부모 클래스 확장 및 부모 클래스 매개 변수와 이 매개 변수 사용을 선언하는 생성자만 사용하십시오.super().

  1. 부모 클래스:
@Component({
  selector: 'teams-players-box',
  templateUrl: '/maxweb/app/app/teams-players-box.component.html'
})
export class TeamsPlayersBoxComponent {
  public _userProfile: UserProfile;
  public _user_img: any;
  public _box_class: string = "about-team teams-blockbox";
  public fullname: string;
  public _index: any;
  public _isView: string;
  indexnumber: number;

  constructor(
    public _userProfilesSvc: UserProfiles,
    public _router: Router,
  ){}
  1. 자녀 클래스:
@Component({  
  selector: '[teams-players-eligibility]',  
  templateUrl: '/maxweb/app/app/teams-players-eligibility.component.html'  
})  
export class TeamsPlayersEligibilityComponent extends TeamsPlayersBoxComponent {  
  constructor (public _userProfilesSvc: UserProfiles,
    public _router: Router) {  
      super(_userProfilesSvc,_router);  
    }  
  }

언급URL : https://stackoverflow.com/questions/36475626/how-to-extend-inherit-components