I would expose it as an Observable and subscribe to it where desired. I still think any interaction with the view layer should be done by view layer objects - i.e. pages, not services.
Hi Guys,
I had this same problem and i fix that with event
In my service:
@Injectable()
export class HttpClient {
constructor(
public events: Events
) {}
...
public error(err:any):any{
if(err.status == 401){
this.events.publish('session:expired');
}
}
And i put a subscriber in my app.component:
@Component({
selector: 'main-app',
templateUrl: '../pages/main/main.html'
})
export class MainApp {
rootPage;
constructor(
public events: Events
){
events.subscribe('session:expired', () => {
this.rootPage = LoginPage;
});
}
}
With that i could fix this problem of session expired and put the login page in my app!
I hope it is useful in some way.
In general, I like your approach. A couple of things:
- Observables > Events
- While tempting,
HttpClient
is a bad name choice, because it is going to clash with the new API in Angular 4.3.
I think I put something together today that MIGHT be relevant. Seems pretty similar to a couple other posts, but might provide something extra.
In my Component, checking login status.
import { Component } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { AuthService } from '../../providers/auth-service';
import { NavController, NavParams } from 'ionic-angular';
import { ImagePage } from '../../pages/image-page/image-page';
export interface User {
email: string,
emailVerified: boolean,
uid: string
}
@Component({
selector: 'page-login-page',
templateUrl: 'login-page.html',
})
export class LoginPage {
private loginForm: FormGroup;
userLoggedOut: boolean;
returnedUser: User;
constructor( private navCtrl: NavController, private authService: AuthService, private formBuilder: FormBuilder) {
this.loginForm = this.formBuilder.group({
email: ['', Validators.compose([Validators.required])],
password: ['', Validators.compose([Validators.required])],
})
}
checkAuthState(){
let authState = this.authService.returnAuthState();
let condition = authState.subscribe((user) => {
if (user) {
this.returnedUser = {
email: user.email,
emailVerified: user.emailVerified,
uid: user.uid
};
this.navCtrl.push(ImagePage,{
user: this.returnedUser
})
}
condition.unsubscribe();
})
}
My Provider, AuthService, with everything but the example of passing the observable from provider to component.
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
import { AngularFireAuth } from 'angularfire2/auth';
import { AlertController } from 'ionic-angular';
export interface RegisteredUser {
email: string,
password: string,
emailVerified: boolean
}
interface CheckedUser {
validityCheck: string,
first_name: string,
last_name: string,
email: string,
password: string
}
@Injectable()
export class AuthService {
validityValue: boolean;
registeredUser: RegisteredUser;
constructor( private alertCtrl: AlertController, private afAuth: AngularFireAuth, public http: Http) {
}
returnAuthState(){
let x = this.afAuth.authState;
return x;
}
}
Well⦠the name is only a sample. I donāt copied the code, i typed here only to explain!
I dont remember what name i used in my project. HttpService maybe.
I agree with your concernā¦
I use constants to reduce the problem with meaning typos. But all others questions is a problemā¦
But, is better than use observable in many classes (i dont like that). The event i use only once. I wil think about how i can do better with observable.
Interesting that I happened to get a notification about this thread at the same time I actually had to deal with a similar problem. When I got a 401 error I needed to be able to trigger navigation app wide. Because of the third-party security plugin I was using, I didnāt have much choice and had to trigger navigation from a service. Again Iāll say this is something you should approach with caution, but I think there are valid use cases.
This also only changes the root navigation, which for something like an auth screen is almost always what you want, but this isnāt a universal catch all.
To navigate from a service, you can simply inject App into your service, and from there get the root nav.
this.app.getRootNav().setRoot(LoginComponent);
Again, you shouldnāt just try to blindly use this, and youāll run a great risk of introducing circular dependency issues that stop your app from runningā¦but in my case I actually needed to do this and there was no real way around it. Soā¦here it is . Use with caution.
Why couldnāt you expose an authorization notifier in the injectable, subscribe to it in the app component, call next(false)
on it whenever you get a 401, and do all the actual nav interaction in the app component?
I could, youāre right, but, Iām giving the simplified version. This plugin could trigger multiple different navigations, not just an invalid token, but it also triggered navigation to different steps of the registration process. Of course I think thatās a fault of this plugin, but I will say that if you have multiple different semi-complex navigation you need to trigger from a service, to me it makes more sense to keep that navigation logic in a relevant place (security.service.ts) instead of in just a base component (app.component).
There are other options too, maybe making a base security component that had the nav logic in there. And overall I agree with you, thatās why I put the āuse with cautionā warning there. Itās a solution if someone needs it, but in most cases it certainly isnāt the best solution.
Iām probably just jaded and cynical, but it seems like once every couple of days I see somebody posting a āthanks for this awesome solutionā in response to a comment in a 3-month-old thread that did or at least should have had that warning in it.
I guess if somebodyās hell-bent on taking out a monster loan at the Bank of Techdebt, nothing I say will convince them otherwise.
Found this on google so thatās why Iām posting.
Was trying to build a logout service. Both the menu (from app.component) and the back button on our root page both needed to call this same service (popup a box asking if they really want to log out, call the logout ws and redirect to the login page). Trying to use NavController from the service is where I ran into problems.
I Like the Event solution that @luizadolpho gave. I just have all the code in app.component (where Navigation is at anyways) Set up an extra event wrapper.