Set root of nav via provider to be a page of which said provider is used in = error


#1

That’s the shortest version of it, which I imagine none of you understand, so let me get a bit more into detail. First, I’d like to point out that I consider myself fairly new to Ionic and Angular, so there’s probably a bunch of terms etc. I don’t understand just yet :stuck_out_tongue: Just keep that in mind when responding.

So, basic setup:

|-- app
|   |-- app.component.ts
|-- pages
|   |-- login
|       |-- login.ts
|-- providers
    |-- user-service.ts

Those are the files that matter, I believe. Quick explanation of the issue and then I’ll paste some of the code in here: The root page I am setting in the app.component.ts is the login page. The login page imports the UserService provider, which on launch does a quick check of the session information to determine whether or not to redirect you away from the login page. The same function is used to redirect you TO the login page, if the app says you are logged in but the session check says otherwise. That is where the error occurs, as soon as I add the following to my UserService

if(this.loggedIn) this.app.getRootNav().setRoot(LoginPage);

I get the following error:

Uncaught Error: Can't resolve all parameters for LoginPage: (NavController, LoadingController, AlertController, TranslateService, ?, SimpleStorage)

My assumption is that it’s because it loops somehow, and I have no idea how to get around it…

login.ts

import { Component } from '@angular/core';
import { NavController, LoadingController, AlertController } from 'ionic-angular';

import { TranslateService } from 'ng2-translate';
import { UserService } from '../../providers/user-service';
import { SimpleStorage } from '../../providers/storage-service';

@Component({
	selector: 'page-login',
	templateUrl: 'login.html'
})
export class LoginPage {
	constructor(
		public navCtrl: NavController, 
		public loadingCtrl: LoadingController, 
		public alertCtrl: AlertController, 
		private translate: TranslateService,
		public userService: UserService,
		public simpleStorage: SimpleStorage
	) {}
}

This page is calling the login function in UserService

user-service.ts

import { Injectable } from '@angular/core';
import { App, LoadingController, AlertController, Platform } from 'ionic-angular';
import 'rxjs/add/operator/map';

import { TranslateService } from 'ng2-translate';
import { BackendService } from '../providers/backend-service';
import { SimpleStorage } from '../providers/storage-service';

import { NewsPage } from '../pages/news/news';
import { LoginPage } from '../pages/login/login';

@Injectable()
export class UserService {
	public loggedIn: boolean = false;

	constructor(
		public app: App,
		public platform: Platform, 
		public loadingCtrl: LoadingController, 
		public alertCtrl: AlertController, 
		public translate: TranslateService,
		public backendService: BackendService,
		public simpleStorage: SimpleStorage
	) {
		platform.ready().then(() => {
			this.checkSession();
		});
	}

	checkSession(silent: boolean = false) {
		this.simpleStorage.getAsyncList(["user.sessid", "user.uid"]).then(
			result => {
				if(!result) {
					let loader = this.loadingCtrl.create({
						content: this.translate.instant("pages.login.logging_in"),
						spinner: "crescent"
					});
					if(!silent) loader.present();
					this.backendService.call(
						{
							**data removed**
						}
					).subscribe(
						result => {
							if(!silent) {
								loader.dismiss();
								this.loggedIn = true;
								this.app.getRootNav().setRoot(NewsPage);
							}
						},
						err => {
							this.checkSessionFailed();
							if(!silent) loader.dismiss();
							let alert = this.alertCtrl.create({
								title: this.translate.instant("providers.UserService.session_expired.title"),
								subTitle: this.translate.instant("providers.UserService.session_expired.message"),
								buttons: [this.translate.instant("general.confirm.ok")]
							});
							alert.present();
						}
					);
				} else {
					this.checkSessionFailed();
				}
			}
		);
	}

	checkSessionFailed() {
		this.simpleStorage.removeList(["user.fullname", "user.firstname", "user.lastname", "user.sessid"]);
		if(this.loggedIn) this.app.getRootNav().setRoot(LoginPage);
		this.loggedIn = false;
	}
}

As already mentioned, as soon as I add in the 4th last line it gives me the error

I have removed parts of the code I decided weren’t necessary, if you need anything else just let me know

Thanks in advance!


#2

That error means that your Dependency Injection service has failed to resolve all the parameters in your Login.ts constructor. My guess is, that you’ve forgotten to add one of your providers, to the provider section in your app.module.ts, so even though it’s recognized internal to the TS File, it’s not recognized by the DI service.

Check to make sure that your Providers section of app.module.ts contains all the providers you’re declaring.


#3

I think you’re also going about setting your root the hard way. Check this out

constructor(private nav: NavController)
...
..

then, anywhere in your TS you can do

  this.nav.setRoot(Dashboard);

#4

Hi bsampica,

This is what is in my app.module.ts:

providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}, BackendService, Storage, SimpleStorage, AdvancedStorage, UserService, ConfigService]

Everything is listed :s it also works just fine when I comment out the line with LoginPage in it (well, except for the redirecting part obviously) Also, when using NavController I get this error:

Uncaught (in promise): Error: Error in ./MyApp class MyApp_Host - inline template:0:0 caused by: No provider for NavController!

Which is why I resorted to using the app to get the nav

Edit: Also, using the NavController anywhere else but in my providers works just fine, and then I’m doing it the way you described :slight_smile:


#5

I’ve never yet seen a situation where trying to access view-layer stuff (like NavController) from an injected provider makes sense. I think it’s always better to expose Observables from providers that simply tell whoever cares “hey, we are now authenticated” or “now we’re not”. Components (typically the app component) subscribe to these and call setRoot appropriately.