Angular 2.3 组件继承和依赖注入

Posted

技术标签:

【中文标题】Angular 2.3 组件继承和依赖注入【英文标题】:Angular 2.3 Component Inheritance and Dependency Injection 【发布时间】:2017-05-24 10:53:33 【问题描述】:

如何使用新的 Angular 2.3 组件继承在子组件和父组件之间共享依赖注入。

例如我想将 AlertService 下移到父组件中,并将 TraingCompanyService 留在派生组件中

当前组件

@Component(
    selector: 'wk-training-company-edit',
    template: require('./edit.html')
)
export class TrainingCompanyEditComponent implements OnInit, OnDestroy 

    constructor(
                private alert: AlertService,
                private trainingCompanyService: TrainingCompanyService
                ) 

    

重构组件 (V1)

在派生类的构造函数中调用this之前必须调用super

@Component(
    selector: 'wk-training-company-edit',
    template: require('./edit.html')
)
export class TrainingCompanyEditComponent extends BaseAdminEditComponent implements OnInit, OnDestroy 

    constructor(
                private alert: AlertService,
                private trainingCompanyService: TrainingCompanyService
                ) 

        // Error: Super must be called before calling this in the constructor of the derived class
        super(this.alert);
    


export class BaseAdminEditComponent 

    constructor(private alert: AlertService) 
    

    protected handleSaveError(error: any) 

        if (error.message) 
            if (error.errors && _.isArray(error.errors) && error.errors.length > 0) 
                this.alert.error(_.join(error.errors, '\n'), error.message);
            
            else 
                this.alert.error(error.message);
            
        
    

重构组件 (V2)

类 TrainingCompanyEditComponent 错误地扩展了基类 BaseAdminEditComponent,类型有单独的私有属性 'alert' 声明

@Component(
    selector: 'wk-training-company-edit',
    template: require('./edit.html')
)
export class TrainingCompanyEditComponent extends BaseAdminEditComponent implements OnInit, OnDestroy 

    // Class TrainingCompanyEditComponent incorrectly extends base class BaseAdminEditComponent, types have seperate declarations of private property 'alert'
    constructor(
                private alert: AlertService,
                private trainingCompanyService: TrainingCompanyService
                ) 

        // alert instead of this.alert
        super(alert);
    


export class BaseAdminEditComponent 

    constructor(private alert: AlertService) 
    

    protected handleSaveError(error: any) 

        if (error.message) 
            if (error.errors && _.isArray(error.errors) && error.errors.length > 0) 
                this.alert.error(_.join(error.errors, '\n'), error.message);
            
            else 
                this.alert.error(error.message);
            
        
    

重构组件 (V3)

这行得通,只是想知道它是否是最好的技术

@Component(
    selector: 'wk-training-company-edit',
    template: require('./edit.html')
)
export class TrainingCompanyEditComponent extends BaseAdminEditComponent implements OnInit, OnDestroy 

    // Class TrainingCompanyEditComponent incorrectly extends base class BaseAdminEditComponent, types have seperate declarations of private property 'alert'
    constructor(
                private alert: AlertService,
                private trainingCompanyService: TrainingCompanyService
                ) 

        // alert instead of this.alert
        super(alert);
    


export class BaseAdminEditComponent 

    // Create a private variable with a different name, e.g. alert2
    private alert2: AlertService;

    constructor(alert: AlertService) 
        this.alert2 = alert;
    

    protected handleSaveError(error: any) 

        if (error.message) 
            if (error.errors && _.isArray(error.errors) && error.errors.length > 0) 
                this.alert2.error(_.join(error.errors, '\n'), error.message);
            
            else 
                this.alert2.error(error.message);
            
        
    


【问题讨论】:

你试过super(alert);? 【参考方案1】:

只需将派生类中构造函数参数的访问修饰符设置为与基类中相同的级别即可。即

基类

import * as _ from "lodash";

import AlertService from '../common/alert/alert.service';

export class BaseAdminEditComponent 

    constructor(protected alert: AlertService)  

    protected handleSaveError(error: any) 

        if (error.message) 
            if (error.errors && _.isArray(error.errors)) 
                console.error(error.errors);
            
            this.alert.error(error.message);
        
    

派生类

@Component(
    selector: 'wk-training-company-edit',
    template: require('./edit.html')
)
export class TrainingCompanyEditComponent extends BaseAdminEditComponent 

    trainingCompany: TrainingCompany;

    trainingCompanyId: number;

    constructor(
        protected alert: AlertService,
        private validation: ValidationService,
        private trainingCompanyService: TrainingCompanyService) 

        super(alert);

        // Other Constructor Code Here
    

【讨论】:

这增加了什么没有被接受的答案? 嗯,这意味着您可以使用 Typescript 语法糖——您不再需要手动分配构造函数参数 也许一些解释什么是答案的本质会有所帮助。我没找到。 在接受的答案中,大卫说不要使用构造函数参数语法糖很重要。事实并非如此。如果您确保基类和派生类中的访问修饰符相同,您仍然可以使用语法糖,这意味着减少冗长,因为您不再需要分配构造函数参数。 我明白了。谢谢你的解释。【参考方案2】:

我终于找到了可行的模式,重要的是不要使用 Radim 在构造函数中提到的私有(语法糖模式)。

我将警报服务设置为基类上的受保护属性。

将基本事件处理程序绑定到此handlerSaveError.bind(this) 很重要

最终的工作代码在这里。

基类

import * as _ from "lodash";

import AlertService from '../common/alert/alert.service';

export class BaseAdminEditComponent 

    protected alert: AlertService;

    constructor(alert: AlertService) 
        this.alert = alert;
    

    protected handleSaveError(error: any) 

        if (error.message) 
            if (error.errors && _.isArray(error.errors)) 
                console.error(error.errors);
            
            this.alert.error(error.message);
        
    

组件实例类

@Component(
    selector: 'wk-training-company-edit',
    template: require('./edit.html')
)
export class TrainingCompanyEditComponent extends BaseAdminEditComponent 

    trainingCompany: TrainingCompany;

    trainingCompanyId: number;

    constructor(alert: AlertService, // Don't use private property
                private validation: ValidationService,
                private trainingCompanyService: TrainingCompanyService) 

        super(alert);

        // Other Constructor Code Here
    

    onSave($event) 

        console.log('Save TrainingCompany');

        this.trainingCompany = TrainingCompany.fromJson(this.form.value);

        console.log(JSON.stringify(this.trainingCompany, null, 2));

        var isNew = _.isNil(this.trainingCompany.id);

        this.trainingCompanyService
            .upsert$(this.trainingCompany)
            .subscribe((response: EntityResponse<TrainingCompany>) => 

                try 
                    this.alert.success('TrainingCompany updated');

                    this.modelChange.fire('training-company', isNew ? 'new' : 'update', this.trainingCompany);
                
                catch (e) 
                    console.error(e);
                    throw e;
                
            , this.handleSaveError.bind(this)); // Common Error Handler from base class. NOTE: bind(this) is required

    

【讨论】:

【参考方案3】:

带有类似这样修饰符关键字的构造函数定义

export class TrainingCompanyEditComponent 
    extends BaseAdminEditComponent implements OnInit, OnDestroy 

    constructor(
        private alert: AlertService,
        private trainingCompanyService: TrainingCompanyService
    ) 
    
    ...

只是更详细的语法糖:

export class TrainingCompanyEditComponent 
    extends BaseAdminEditComponent implements OnInit, OnDestroy 

    private alert: AlertService,
    private trainingCompanyService: TrainingCompanyService

    constructor(
        alert: AlertService,
        trainingCompanyService: TrainingCompanyService
    ) 
        this.alert = alert; // assign incoming value to member
        this.trainingCompanyService = trainingCompanyService;
    
    ...

所以,这是在后台。我们只能在调用 super 后执行该分配。这就是为什么我们必须将使用 this.alert (访问成员) 的调用更改为 alert (传递传入值)

    constructor(
        private alert: AlertService,
        private trainingCompanyService: TrainingCompanyService
    ) 
        //super(this.alert);
        super(alert); // this.alert was not assign yet

        // next ?
        // the this.alert will be assigned for us...
    

玩这个adjusted definition here

【讨论】:

我已经更新了我上面的问题,以考虑到警报与 this.alert 的问题,但仍然存在错误。本质上,您在此处的此示例给出了以下错误“类型具有私有属性'alert'的单独声明” 我为您创建了一个游乐场示例。它应该显示这些调整将如何使其工作......另外,我首先定义了基础......【参考方案4】:

尝试将构造函数移动到 BaseAdminEditComponent 中,而不是覆盖 TrainingCompanyEditComponent 中的构造函数。

@Component(
    selector: 'wk-training-company-edit',
    template: require('./edit.html')
)
export class TrainingCompanyEditComponent extends BaseAdminEditComponent implements OnInit, OnDestroy 

    


export class BaseAdminEditComponent 

    constructor(private alert: AlertService,
                private trainingCompanyService: TrainingCompanyService) 
    

    protected handleSaveError(error: any) 

        if (error.message) 
            if (error.errors && _.isArray(error.errors) && error.errors.length > 0) 
                this.alert.error(_.join(error.errors, '\n'), error.message);
            
            else 
                this.alert.error(error.message);
            
        
    

【讨论】:

以上是关于Angular 2.3 组件继承和依赖注入的主要内容,如果未能解决你的问题,请参考以下文章

在Angular2中,使用继承还是注入依赖?

Angular 1.5 组件依赖注入

Angular 1.5 组件/需要 $log 的依赖注入

翻译对比Angular1和Angular2中的依赖注入

如何使用 Angular 中的依赖注入将属性指令实例传递给嵌套组件

如何在 Angular-Fullstack 生成的 Angular 1.5 组件中注入依赖项