How can I share dependency injection between child and parent components with the new Angular 2.3 Component Inheritance.
e.g. I want to move AlertService down into the parent component and leave TraingCompanyService in the derived component
Current Component
@Component({
selector: 'wk-training-company-edit',
template: require('./edit.html')
})
export class TrainingCompanyEditComponent implements OnInit, OnDestroy {
constructor(
private alert: AlertService,
private trainingCompanyService: TrainingCompanyService
) {
}
}
Refactored Components (V1)
Super must be called before calling this in the constructor of the derived class
@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);
}
}
}
}
Refactored Components (V2)
Class TrainingCompanyEditComponent incorrectly extends base class BaseAdminEditComponent, types have seperate declarations of private property '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);
}
}
}
}
Refactored Components (V3)
This works, just wondering if it is the best technique
@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);
}
}
}
}
Just set the access modifier of the constructor parameter in the derived class at the same level as that in the base class. i.e.
Base Class
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);
}
}
}
Derived Class
@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
}
}
I finally worked out the pattern that works, it is important to not use the private (syntacting suger pattern) that Radim mentioned in the constructors.
I made the alert service a protected property on the base class.
And it is important to bind the base event handler to this handlerSaveError.bind(this)
Final working code is here.
Base Class
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 Instance Class
@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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With