1

I am using ionic4 and Angular 7.2.2, and I created a base component class to be extended by other components and not have to repeat a lot of things that I always use. In order to avoid the constructor injection, and therefore be forced to pass again the params to the base construtor in th extended class, I am using Injector.get.

The problem that experience is that, when used it this way, a lot of Angular or Ionic injectors that work fine if injected in the child constructor, do not work or return empty values when used in the child, using the parent's reference.

I am sure there is something I am missing out, because upon inspection of the injector when using the one from my base class and the one from the child, they are different.

Uppon execution, and going to: http://localhost:8101/home/4

This is the relevant code:

app-routing.module.ts



const routes: Routes = [
    {path: '', redirectTo: 'home/4', pathMatch: 'full'},
    {path: 'home/:id', loadChildren: './home/home.module#HomePageModule'},
];

home.page.ts

import {Component} from '@angular/core';
import {BasePage} from '../../tests/base.comp.test';
import {ActivatedRoute} from '@angular/router';

@Component({
    selector: 'app-home',
    templateUrl: 'home.page.html',
    styleUrls: ['home.page.scss'],
})
export class HomePage extends BasePage {

    constructor(public ar: ActivatedRoute) {
        super();


        this.activatedRoute.params.subscribe(
            (result) => {
                console.log(`PARAMS FROM inherited activatedRoute-> `, result); // -> outputs {} WRONG!

            }
        );

        ar.params.subscribe(
            (result) => {
                console.log(`Params from direct injection-> `, result); // -> outputs {id: "4"} Good!
            }
        );


    }

}

home.module.ts

import {NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
import {HomePage} from './home.page';
import {BasePageModule} from '../../tests/base.test.module';

@NgModule({
    imports: [
        BasePageModule,
        RouterModule.forChild([
            {
                path: '',
                component: HomePage
            }
        ])
    ],
    declarations: [HomePage]
})
export class HomePageModule {
}

base.test.module.ts

import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {IonicModule} from '@ionic/angular';
import {FormsModule} from '@angular/forms';
import {BasePage} from './base.comp.test';

@NgModule({
    imports: [
        CommonModule,
        IonicModule,
        FormsModule,
    ],
    declarations: [BasePage],
    providers: [],
    exports: [
        CommonModule,
        IonicModule,
        FormsModule
    ]
})
export class BasePageModule {
}

base.comp.test.ts

import {Component, Injector} from '@angular/core';
import {AppInjectorTest} from './app.injector.test';
import {ActivatedRoute, Router} from '@angular/router';

@Component({
    selector: 'app-base',
    template: '',
})
export class BasePage {

    public injector: Injector;
    public activatedRoute: ActivatedRoute;

    constructor() {
        const injector = AppInjectorTest.getInjector();
        this.activatedRoute = injector.get(ActivatedRoute);
    }

}

app.injector.test.ts

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

export class AppInjectorTest {

    private static injector: Injector;

    static setInjector(injector: Injector) {
        AppInjectorTest.injector = injector;
    }

    static getInjector(): Injector {
        return AppInjectorTest.injector;
    }

}

app.component.ts

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

import {Platform} from '@ionic/angular';
import {SplashScreen} from '@ionic-native/splash-screen/ngx';
import {StatusBar} from '@ionic-native/status-bar/ngx';
import {AppInjectorTest} from '../tests/app.injector.test';

@Component({
    selector: 'app-root',
    templateUrl: 'app.component.html'
})
export class AppComponent {
    constructor(
        private platform: Platform,
        private splashScreen: SplashScreen,
        private statusBar: StatusBar,
        private injector: Injector
    ) {
        this.initializeApp();
        AppInjectorTest.setInjector(injector);
    }

    initializeApp() {
        this.platform.ready().then(() => {
            this.statusBar.styleDefault();
            this.splashScreen.hide();
        });
    }
}

Martin
  • 431
  • 1
  • 6
  • 12

2 Answers2

2

For now it seems that the only workaround we want to do this is to re-inject the injector from the child into the parent. It is not as clean as keeping the constructor completely empty, but, at least, you can extend and reuse a lot of logic from a parent component in a neat way.

child.comp.ts (that extends from parent)


    constructor(public injector: Injector) {
        super(injector);
    }

(...)

parent.comp.ts

(...)
    constructor(public injector: Injector) {
         this.activatedRoute = injector.get(ActivatedRoute);
    }

(...)

Martin
  • 431
  • 1
  • 6
  • 12
1

there is nothing wrong with your static injector but it related how activatedRoute work , ActivatedRoute contains the information about a route associated with a component. loaded in an outlet doc , thay whay the injected activatedRoute (component) work instad of base injected activatedrouteservice

when using a parametrized route which targets a component, only this component can access those parameters.

check this question has similar problem How to retrieve a route param using Injector.get(ActivatedRoute)?

Muhammed Albarmavi
  • 23,240
  • 8
  • 66
  • 91
  • So it is impossible to extend a basecomponent ans have the reference to activatedRoute automatically from the parent? – Martin Jul 03 '19 at 08:33
  • unfortunately so far no m you need to remove the activatedRoute fromn the base class – Muhammed Albarmavi Jul 03 '19 at 08:34
  • I asked this about activatedRoute, but I am also having problems with other services like ModalController from ionic. I guess it is the same problem then? – Martin Jul 03 '19 at 08:36
  • sorry I have no idea about ionic I hasn't use it ,so I don't know if it has similar restriction – Muhammed Albarmavi Jul 03 '19 at 08:43
  • The only thing that I know works, is to re-inject the injector into the super, coming from the child. Then, it works. I wanted to avoid that part, for elegance. – Martin Jul 03 '19 at 09:31
  • In the child: constructor(public injector: Injector) { super(injector); (...) – Martin Jul 03 '19 at 09:31
  • I marked your answer as valid, but I replied to myself also, in order to make it clear for other people facing the same problem. I am going to to go this way to avoid problems with the idiosincrasy of Angular, even if it is not as smart as having a way to remove the need for injecting in constructors. – Martin Jul 03 '19 at 10:10