0

Hi I am trying to create a component that extends from another component, but code become very clumsy, as I must propagate constructor parameter to the child components. Is there a better way to structure the following code?

The base class is:

import { alerts } from './alerts';
import { BaseFirebaseDaoService } from './../providers/base-firebase-dao-service';
import { Injectable,Component,Inject } from '@angular/core';
import { NavParams } from 'ionic-angular';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { FirebaseListObservable } from 'angularfire2';

@Injectable()
@Component({   
     templateUrl: './dummy.html'
})
export class BaseFormHandler<T extends BaseFirebaseDaoService> {
  form: FormGroup;
  item;

  dao:T;
  constructor(public navParams: NavParams,
    public fb: FormBuilder, public _alerts:alerts) {
    this.item = this.navParams.get("item");    
    this.buildForm();
  }

  buildForm(){
    //to be ovveride in sub clusses.
  }

  remove(){    
    this.dao.remove(this.item.$key)
    .then((a:void) => {
      this._alerts.presentToast("removed success");
    })
    .catch((a:Error) => {this._alerts.presentToast("remove fail")});
  }
  submit() {
    let key = this.item.$key;
    this.dao.update(key, this.form.value);
  }
}

This is the extending class:

import { alerts } from './../../shared/alerts';
import { BaseFormHandler } from './../../shared/base-form-handler';
import { Component } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { MemberService } from '../../providers/member-service';
import { NavParams } from 'ionic-angular';
import { AngularFire } from 'angularfire2';

@Component({
  templateUrl: 'members-form-page.html'
})
export class MembersFormPage extends BaseFormHandler<MemberService>{
  constructor(public navParams: NavParams,
    public fb: FormBuilder, af: AngularFire, public _alerts: alerts) {
    super(navParams, fb, _alerts);
    this.dao = new MemberService(af);
  }
  buildForm() {
    this.form = this.fb.group({
      name: [this.item.name, Validators.required],
      email: [this.item.email, Validators.required]
    });
  }
}

So I must always send (and import) the constructor's parameters public navParams: NavParams, public fb: FormBuilder, public _alerts:alerts from all sub classes, although the sub-class not using them. This list can be longer… Any alternative way for such implementation?

2
  • It's the best way to do it, you have another choice but you will lose the DI functionality. You can use a service locator strategy to avoid sending everything to the parent. Check the ReflectiveInjector from @angular/core. The idea is to let the base component find the service that you want to use and return it. Commented Mar 2, 2017 at 18:40
  • Parent class constructor has to be invoked and all the paramters have to be passed too. The only way I am aware of to shorten this code a bit is to skip accessors (public, protected, private) in child class constructors, as those values will be assigned in parent class anyway. Commented Mar 2, 2017 at 18:45

1 Answer 1

1

Don't know if you still need this, but I had the same problem today and I found some help in the following 2 links:

Getting instance of service without constructor injection

https://github.com/angular/angular/issues/4112#issuecomment-153811572

Basically, I've created a global variable that holds the Angular Injector, that I use in the "Base" class to resolve all the dependencies. This way I don't need to inject them in each Component that extends that base class.

Base class:

export class BaseClass  {

    protected aService: AService;
    protected bService: BService;

    constructor() {
        this.aService = appInjector().get(AService);
        this.bService = appInjector().get(BService);
    }

    ...

}

AppInjector definition:

import {Injector} from '@angular/core';

let appInjectorRef: Injector;
export const appInjector = (injector?: Injector):Injector => {
    if (injector) {
        appInjectorRef = injector;
    }
    return appInjectorRef;
};

I initialize the AppInjector global var after "root" module boostrap (in my case in the app.main.ts):

platformBrowserDynamic().bootstrapModule(CpcAppModule)
.then((modRef) => {
    appInjector(modRef.injector);
    console.log(`Application started`)
})
.catch((err) => console.error(err));

Hope it helps.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.