如何扩展/inheritanceAngular2组件?

我想为已经部署在Angular 2中的一些组件创build扩展,而不必几乎完全重写它们,因为基本组件可能发生变化,并希望这些变化也反映在派生的组件中。

我创build了这个简单的例子,试图解释更好的问题:

使用以下基本组件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); } } 

您是否想创build另一个派生组件,例如,在示例颜色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完整的工作示例

注:显然这个例子很简单,可以解决,否则不需要使用inheritance,但它只是为了说明真正的问题。

问题

正如您在派生组件app/my-panel.component.ts的实现中所看到app/my-panel.component.ts ,大部分实现都是重复的,并且单个部分实际上inheritance了BasePanelComponent class ,但@Component基本上必须完全重复,不只是改变的部分,作为selector: 'my-panel'

通过某种方式来实现组件Angular2的字面完全inheritance,inheritance标记/注释的class定义,例如@Component

编辑1 – function请求

function请求angular2添加到GitHub上的项目: 扩展/inheritanceangular2组件注释#7968

编辑2 – closures的请求

这个请求被closures了, 出于这个原因 ,简单的说就不知道如何合并装饰器了。 离开我们没有select。 所以我的意见是引用在这个问题上 。

Angular 2版本2.3刚刚发布,它包括本地组件inheritance。 它看起来像你可以inheritance和覆盖任何你想要的,除了模板和样式。 一些参考:

  • Angular 2.3的新特性

  • 在angular度的组件inheritance2

  • 一个演示组件inheritance的Plunkr

替代scheme:

Thierry Templier的这个答案是解决问题的另一种方法。

在Thierry Templier问了一些问题之后,我来到了下面的工作例子,这个例子符合我的期望,作为这个问题中提到的inheritance限制的替代:

1 – 创build自定义装饰器:

 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装饰器的基础组件:

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

3 – 使用@CustomComponent装饰器的子组件:

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

普朗克与完整的例子。

据我所知组件inheritance还没有在Angular 2中实现,我不确定他们是否有计划,但是因为Angular 2使用了typescript(如果你已经决定走这条路线),你可以使用类inheritance通过做class MyClass extends OtherClass { ... } 。 对于组件inheritance,我build议通过https://github.com/angular/angular/issues并提交function请求来参与Angular 2项目!

更新

从2.3.0-rc.0开始支持组件inheritance

原版的

到目前为止,对我来说最方便的是将模板和样式保存到单独的*html*.css文件中,并通过templateUrlstyleUrls指定它们,因此很容易重用。

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

现在TypeScript 2.2支持通过Classexpression式的Mixins,我们有更好的方法来表示组件上的Mixins。 请注意,您还可以使用组件inheritance,因为angular2.3( 讨论 )或自定义装饰器在此处的其他答案中讨论。 不过,我认为Mixin有一些属性,使它们更适合跨组件重用行为:

  • Mixin组成更灵活,也就是说,您可以在现有组件上混合搭配Mixins,或者将Mixin组合起来形成新的组件
  • Mixin组合仍然很容易理解,这要归功于类inheritance层次的明显线性化
  • 您可以更轻松地避免装饰器和困扰组件inheritance的注释问题( 讨论 )

我强烈build议您阅读上面的TypeScript 2.2公告,了解Mixins如何工作。 angular度GitHub问题中的链接讨论提供了更多的细节。

你需要这些types:

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

然后你可以像这样声明一个Mixin Destroyable mixin帮助组件跟踪需要在ngOnDestroy处理的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 } }; } 

要将Destroyable混入Component ,可以像这样声明组件:

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

请注意,只有当您想要extend Mixin组合时,才需要MixinRoot 。 你可以很容易地扩展多个混合,例如A extends B(C(D)) 。 这是我在上面讨论的mixins的明显线性化,例如,您正在有效地编写一个inheritance层次结构A -> B -> C -> D

在其他情况下,例如,当您想要在现有的类上编写Mixin时,可以像这样应用Mixin:

 const MyClassWithMixin = MyMixin(MyClass); 

不过,我发现第一种方法对ComponentsDirectives效果最好,因为它们也需要用@Component@Directive进行修饰。

如果有人正在寻找更新的解决scheme,费尔南多的答案是非常完美的。 除了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! } 

我知道这不能回答你的问题,但我真的认为应该避免inheritance/扩展组件 。 这是我的推理:

如果由两个或多个组件扩展的抽象类包含共享逻辑:使用一个服务,甚至创build一个可以在这两个组件之间共享的新的打字稿类。

如果抽象类…包含共享variables或onClicketc函数,那么在两个扩展组件视图的html之间将会有重复。 这是不好的做法,共享的HTML需要分解成组件。 这些组件(部件)可以在这两个组件之间共享。

我是否错过了为组件提供抽象类的其他原因?

我最近看到的一个例子是扩展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(); }); } } 

这是因为在整个庞大的代码库中,infiniteSubscriptions.push()仅被使用了10次。 另外,导入和扩展AutoUnsubscribe实际上需要更多的代码,而不仅仅是在组件本身的ngOnDestroy()方法中添加mySubscription.unsubscribe(),这需要额外的逻辑。

 just use inheritance,Extend parent class in child class and declare constructor with parent class parameter and this parameter use in super(). 1.parent class @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, ){} 2.child class @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); } }