Question
我想为已经部署在Angular 2中的一些组件创建扩展,而不必几乎完全重写它们,因为基本组件可能会发生更改,并希望这些更改也反映在其派生组件中 .
Example
I created this simple example to try to explain better my questions:
使用以下基本组件 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";
}
}
Complete working example in Plunker
注意:显然这个例子很简单,可以解决,否则不需要使用继承,但它只是为了说明真正的问题 .
Problem
正如您在衍生组件 app/my-panel.component.ts
的实现中所看到的,大部分实现都是重复的,单个部分真正继承的是 class
BasePanelComponent
,但 @Component
基本上必须完全重复,而不仅仅是更改的部分,如 selector: 'my-panel'
.
Question
以某种方式对组件Angular2进行字面上的完全继承,继承标记/注释的 class
定义,例如 @Component
?
Edit 1 - Feature Request
功能请求angular2添加到GitHub上的项目:Extend / Inherit angular2 components annotations#7968
Edit 2 - Closed Request
由于这个原因,请求已经关闭,暂时不知道如何合并装饰器 . 离开我们没有选择 . 所以我的意见是在问题中引用的 .
10 回答
Alternative Solution:
This answer of Thierry Templier is an alternative way to get around the problem.
在与Thierry Templier提出一些问题之后,我来到了以下工作示例,该示例符合我的期望,作为此问题中提到的继承限制的替代方案:
1 - 创建自定义装饰器:
2 - 带@Component装饰器的基础组件:
3 - 带@CustomComponent装饰器的子组件:
Plunkr with complete example.
Angular 2版本2.3刚刚发布,它包含本机组件继承 . 看起来你可以继承和覆盖你想要的任何东西,模板和样式除外 . 一些参考:
New features in Angular 2.3
Component Inheritance in Angular 2
A Plunkr demonstrating component inheritance
既然TypeScript 2.2支持Mixins through Class expressions,我们有更好的方式在组件上表达Mixins . 请注意,您也可以使用角度继承,因为角度为2.3(discussion)或自定义装饰器,如此处的其他答案中所述 . 但是,我认为Mixins有一些属性使它们更适合重用组件之间的行为:
Mixins组合更灵活,即您可以在现有组件上混合和匹配Mixins,或组合Mixins以形成新组件
Mixin组合仍然易于理解,因为它对类继承层次结构有明显的线性化
您可以更轻松地避免使用装饰器和注释组件继承的注释(discussion)
我强烈建议您阅读上面的TypeScript 2.2声明,以了解Mixins的工作原理 . 角度GitHub问题中的链接讨论提供了更多细节 .
你需要这些类型:
然后你可以声明一个类似于这个
Destroyable
mixin的Mixin,它可以帮助组件跟踪需要在_1440882中处理的订阅:要将
Destroyable
混合到Component
中,您可以像这样声明组件:请注意,只有当您想要
extend
一个Mixin合成时才需要MixinRoot
. 您可以轻松扩展多个mixin,例如A extends B(C(D))
. 这是我上面谈到的mixins的明显线性化,例如你正在有效地组成一个继承层次结构A -> B -> C -> D
.在其他情况下,例如当你想在现有的类上组合Mixins时,你可以像这样应用Mixin:
但是,我发现第一种方式最适合
Components
和Directives
,因为这些也需要用@Component
或@Directive
进行修饰 .update
2.3.0-rc.0支持组件继承
original
到目前为止,对我来说最方便的是将模板和样式保存到单独的
*html
和*.css
文件中,并通过templateUrl
和styleUrls
指定它们,因此它很容易重复使用 .据我所知,组件继承尚未在Angular 2中实现,并且我决定采用该路由)您可以通过执行
class MyClass extends OtherClass { ... }
来使用类继承 . 对于组件继承,我建议通过转到https://github.com/angular/angular/issues并提交功能请求参与Angular 2项目!我知道这不能回答你的问题但是 I really think inheriting / extending components should be avoided . 这是我的推理:
如果由两个或多个组件扩展的抽象类包含共享逻辑:使用服务或甚至创建可在两个组件之间共享的新typescript类 .
如果抽象类...包含共享变量或onClicketc函数,那么两个扩展组件视图的html之间将存在重复 . 这是不好的做法,共享的html需要分解为组件 . 这些组件(部件)可以在两个组件之间共享 .
我错过了为组件提供抽象类的其他原因吗?
我最近看到的一个例子是扩展AutoUnsubscribe的组件:
这是bas,因为在整个大型代码库中,infiniteSubscriptions.push()只使用了10次 . 另外,导入和扩展AutoUnsubscribe实际上需要的代码多于在组件本身的ngOnDestroy()方法中添加mySubscription.unsubscribe(),这无论如何都需要额外的逻辑 .
如果有人正在寻找更新的解决方案,费尔南多的答案非常完美 . 除了
ComponentMetadata
已被弃用 . 使用Component
代替我工作 .完整的Custom Decorator
CustomDecorator.ts
文件如下所示:然后将其导入新组件
sub-component.component.ts
文件并使用@CustomComponent
而不是@Component
,如下所示:组件可以像typescript类继承一样扩展,只需要用新名称覆盖选择器 . 父组件中的所有Input()和Output()属性都正常工作
Update
@Component是装饰者,
在类的声明期间,不在对象上应用装饰器 .
基本上,装饰器将一些元数据添加到类对象中,并且无法通过继承访问 .
如果你想实现Decorator继承,我会建议编写一个自定义装饰器 . 像下面的例子 .
参考:https://medium.com/@ttemplier/angular2-decorators-and-class-inheritance-905921dbd1b7
您可以使用
<ng-content></ng-content>
指令 .例如:
Header Component
Custom Header Component
<header-component>This is header</header-component>
打印 This is header<custom-header-component>This is header</custom-header-component>
打印 This is custom header This is header