How to use ModalController in a separate angular5/ionic4 module


#1

I am trying to create a separate dynamic form module using angular’s ReactiveForms and Ionic (4.7.0).
I am running into an Ionic specific problem with a service that is internal to my DynamicFormsModule and depends on the Ionic ModalController.

When I inject the ModalController in my DynamicFormService I get the following error:

Can't resolve all parameters for DynamicFormComponent(?, [object Object]).

This is the start of my DynamicFormComponent:

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { FormGroup, FormBuilder }   from '@angular/forms';
import { FormConfig } from './models/form-config.interface';
import { DynamicFormService } from './dynamic-form.service';
 

@Component({
  selector: 'dynamic-form',
  templateUrl: './dynamic-form.component.html',
})
export class DynamicFormComponent implements OnInit{

  @Input() datasetSchema: object;
  @Input() formName: string;
  @Output() valueChanged: EventEmitter<any> = new EventEmitter<any>();

  payLoad: string;
  formGroup: FormGroup;
  config: FormConfig;

  constructor(
    public dfs: DynamicFormService,
    private fb: FormBuilder,
  ) {  }
  //... Rest left out for brevity
}

By elimination I got to the point that removing the ModalController from DynamicFormService eliminates the error, so it has to be there. Further reading makes me understand that there is some magic (or a lack of understanding with me) going on where the Ionic bootstrapping makes the ModalController Injectable available to the App without needing to specify it in providers:[] anywhere. But this magic makes it impossible to use it in my sub module so far.
I hope someone can give me some pointers to how I can use ModalController in a module separate from my main app.

This is my DynamicFormService

import { Injectable } from '@angular/core';
import { Validators } from '@angular/forms';
import { FormConfig } from './models/form-config.interface';
import { FieldConfig } from './models/field-config.interface';
import { DynamicSubFormComponent } from './dynamic-subform.component';
import { IntegerValidator } from './fields/validators';
import { ModalController } from 'ionic-angular';

@Injectable()
export class DynamicFormService {
  private modals = {};

  constructor(
    public modalCtrl: ModalController,
  ) {
  }

  addSubForm(sfkey, formConfig) {
    let modal = this.modalCtrl.create(DynamicSubFormComponent, {'formconfig': formConfig}, {showBackdrop: false});
    this.modals[sfkey] = modal;
    return modal;
  }
  // .... Stuff left out for brevity
}

This is the DynamicFormModule:

import { NgModule, ErrorHandler } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule, IonicErrorHandler } from 'ionic-angular';
import { ReactiveFormsModule }          from '@angular/forms';

import { StringFieldComponent } from './fields/stringfield.component';
import { IntegerFieldComponent } from './fields/integerfield.component';
import { NumberFieldComponent } from './fields/numberfield.component';

import { DynamicFormService } from './dynamic-form.service';
import { DynamicFormComponent } from './dynamic-form.component';
import { DynamicSubFormComponent } from './dynamic-subform.component';
import { DynamicFieldDirective } from './dynamic-field.directive';

@NgModule({
  declarations: [
    StringFieldComponent,
    IntegerFieldComponent,
    NumberFieldComponent,
    DynamicFieldDirective,
    DynamicFormComponent,
    DynamicSubFormComponent,
  ],
  providers: [
    DynamicFormService,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ],
  imports: [
    CommonModule,
    IonicModule,
    ReactiveFormsModule,
  ],
  exports: [
    DynamicFormComponent,
  ],
  entryComponents: [
    StringFieldComponent,
    IntegerFieldComponent,
    NumberFieldComponent,
  ]
})
export class DynamicFormModule {
}

And this is my AppModule:

import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { EnviMo } from './app.component';

import { HomePage } from '../pages/home/home';
import { ListPage } from '../pages/list/list';

import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { DynamicFormModule } from '../dynamic-form/dynamic-form.module';

import { PageService } from '../providers/page-service/page-service';

import { SocketIoModule, SocketIoConfig } from 'ng-socket-io';

const config: SocketIoConfig = { url: 'http://127.0.0.1:8080', options: {} };

@NgModule({
  declarations: [
    EnviMo,
    HomePage,
    ListPage,
  ],
  imports: [
    BrowserModule,
    SocketIoModule.forRoot(config),
    DynamicFormModule,
    IonicModule.forRoot(EnviMo)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    EnviMo,
    HomePage,
    ListPage,
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler},
    PageService,
  ]
})
export class AppModule {
  constructor() {
  }
}

#2

I don’t like this design in general, because I think that services should not be interacting with the view layer. If you insist on keeping it, though, one thing you can do is have every function in DynamicFormService that needs a ModalController take one as a parameter, and pass it from a page that can inject one.


#3

I agree about the design. Just today, I redid it and removed the dependency from the service. It fixes all poblems and I can still use the ModalController in my components. Just inject the ModalController in the components instead of the service.