我想为一些已经部署在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 -关闭请求
请求被关闭,因为这个原因,暂时不知道如何合并装饰器将作出。让我们别无选择。所以我的观点在本期杂志上被引用了。
可选择的解决方案:
蒂埃里·坦普利埃的回答是解决这个问题的另一种方法。
在与Thierry 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装饰器的基础组件:
@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() {
}
}
Plunkr的完整示例。
可选择的解决方案:
蒂埃里·坦普利埃的回答是解决这个问题的另一种方法。
在与Thierry 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装饰器的基础组件:
@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() {
}
}
Plunkr的完整示例。
只需使用继承,在子类中扩展父类,并在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);
}
}
如果有人正在寻找一个更新的解决方案,费尔南多的答案几乎是完美的。除了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