Angular 2.3组件继承和依赖注入

9

如何使用新的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)

在派生类的构造函数中调用此函数之前必须先调用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); 吗? - KnowHoper
4个回答

14

只需在派生类构造函数参数中设置访问修饰符,使其与基类相同即可。例如:

基类

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
    }
}

3
这个问题问的是除了已接受答案中所涵盖的内容之外,这个回答添加了什么信息? - Günter Zöchbauer
好的,这意味着您可以使用TypeScript语法糖-您不再需要手动分配构造函数参数。 - Jonesy
也许解释一下答案的本质会有所帮助,我找不到它。 - Günter Zöchbauer
1
在被接受的答案中,David说不使用构造函数参数语法糖很重要。但事实并非如此。如果您确保基类和派生类中的访问修饰符相同,则仍然可以使用语法糖,这意味着减少冗余,因为您不再需要分配构造函数参数。 - Jonesy
我明白了。谢谢你的解释。 - Günter Zöchbauer

8

我最终找到了可行的模式,重要的是不要在构造函数中使用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

    }
}

2
构造函数定义中使用类似这样的修饰符关键字
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...
    }

在这里玩耍


我已经更新了我的问题,考虑到警报与 this.alert 的问题,但仍然存在错误。实际上,您所提供的示例会产生以下错误:“类型具有私有属性'alert'的单独声明”。 - David Cruwys
我为你创建了一个游乐场示例。它应该展示这些调整如何使其工作...另外,我首先定义了基础。 - Radim Köhler

0
尝试将构造函数移动到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);
            }
        }
    }
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接