【问题标题】:Angular 2.3 Component Inheritance and Dependency InjectionAngular 2.3 组件继承和依赖注入
【发布时间】: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);?

标签: angular typescript


【解决方案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);
                }
            }
        }
    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 1970-01-01
      • 2011-12-24
      • 2021-04-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多