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?

Ronen
  • 807
  • 1
  • 13
  • 33
  • 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. – dlcardozo Mar 02 '17 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. – Daniel Kucal Mar 02 '17 at 18:45

1 Answers1

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.

JoGo
  • 796
  • 5
  • 5