我想为一些已经部署在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);
  }
}

你想创建另一个衍生组件,只改变,例如,一个基本组件的行为,在例子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的实现中看到的,大部分实现是重复的,真正继承的部分是类BasePanelComponent,但是@Component必须基本上完全重复,而不仅仅是改变的部分,作为选择器:'my-panel'。

有没有什么方法可以完全继承angular组件,继承标记/注释的类定义,比如@Component?

编辑1 -功能请求

特性请求angular2添加到GitHub上的项目:扩展/继承angular2组件注释#7968

编辑2 -关闭请求

请求被关闭,因为这个原因,暂时不知道如何合并装饰器将作出。让我们别无选择。所以我的观点在本期杂志上被引用了。


当前回答

组件可以像typescript类继承一样扩展,只是您必须用一个新名称覆盖选择器。所有来自父组件的Input()和Output()属性正常工作

更新

@Component是一个装饰器,

装饰符是在类声明期间应用的,而不是在对象上。

基本上,装饰器向类对象添加一些不能通过继承访问的元数据。

如果你想实现装饰器继承,我建议写一个自定义装饰器。类似下面的例子。

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/angular2-decorators-and-class-inheritance-905921dbd1b7

其他回答

让我们来了解一下Angular组件继承系统的一些关键限制和特性。

组件只继承类逻辑:

@Component装饰器中的所有元数据都不会被继承。 组件@Input属性和@Output属性被继承。 组件生命周期不继承。

记住这些特性是非常重要的,所以让我们单独研究每一个特性。

组件只继承类逻辑

当你继承一个组件时,里面的所有逻辑都是平等继承的。值得注意的是,只有public成员被继承,而private成员只能在实现它们的类中访问。

@Component装饰器中的所有元数据都不会被继承

没有元数据被继承的事实乍一看似乎是违反直觉的,但如果你仔细想想,它实际上是完全有道理的。如果你从一个组件继承,比如(componentA),你不会想要从componentA继承的选择器替换被继承的类ComponentB的选择器。对于template/templateUrl以及style/styleUrls也是如此。

组件@Input和@Output属性被继承

这是我非常喜欢Angular中组件继承的另一个特性。简单地说,只要有自定义的@Input和@Output属性,这些属性就会被继承。

组件生命周期不继承

这一部分不是很明显,特别是对那些没有广泛使用过面向对象原则的人来说。例如,假设你有ComponentA,它实现了Angular的许多生命周期钩子中的一个,比如OnInit。如果你创建了ComponentB并继承了ComponentA,那么ComponentA的OnInit生命周期将不会触发,直到你显式地调用它,即使你确实为ComponentB拥有这个OnInit生命周期。

调用超级/基本组件方法

为了从ComponentA中获得ngOnInit()方法,我们需要使用super关键字,然后调用我们需要的方法,在本例中是ngOnInit。super关键字指的是要继承的组件的实例,在本例中是ComponentA。

只需使用继承,在子类中扩展父类,并在super()中使用父类形参声明构造函数和此形参。

父类:

@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,
  ){}

子类:

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

据我所知,在Angular 2中还没有实现组件继承,我不确定他们是否计划这样做,不过既然Angular 2使用了typescript(如果你决定这么做的话),你就可以通过MyClass extends OtherClass{…}。对于组件继承,我建议通过访问https://github.com/angular/angular/issues并提交特性请求来参与Angular 2项目!

如果有人正在寻找一个更新的解决方案,费尔南多的答案几乎是完美的。除了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);
  }
}

然后将它导入到你的新组件subcomponent .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!
}

组件可以像typescript类继承一样扩展,只是您必须用一个新名称覆盖选择器。所有来自父组件的Input()和Output()属性正常工作

更新

@Component是一个装饰器,

装饰符是在类声明期间应用的,而不是在对象上。

基本上,装饰器向类对象添加一些不能通过继承访问的元数据。

如果你想实现装饰器继承,我建议写一个自定义装饰器。类似下面的例子。

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/angular2-decorators-and-class-inheritance-905921dbd1b7