I have an Ionic app that logs in and stores the user data in the Capacitor Storage.
this.httpService.login(this.username, this.password).subscribe(async (user: any) => {
await Preferences.set({ key: 'user', value: JSON.stringify(user)});
this.router.navigate(['']);
});
In my homepage, I want to check if I am logged in (if the Capacitor storage has anything in the user key). If it doesn't, it should go directly to the Login Page, and if it does, it stays where it is. I don't know if this is the best way to do it (maybe there is a better way to load the component in the Angular routing?). What I'm doing now is the following:
I have an user service:
import { Injectable } from '@angular/core';
import { User } from '../models/user';
import { Preferences } from '@capacitor/preferences';
@Injectable({providedIn: 'root'})
export class UserService {
private user: User = new User();
private userSet = false;
constructor() {
Preferences.get({ key: 'user' }).then((data: any) => {
if(data) {
const userObj = JSON.parse(data.value);
this.user = Object.assign(this.user, userObj);
// console.log(this.user);
this.userSet = true;
}
});
}
hasUser() {
return this.userSet;
}
}
And I have that service in my home page, which I check to navigate appropriately:
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { HttpService } from '../services/httpService';
import { UserService } from '../services/userService';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss']
})
export class HomePage implements OnInit {
constructor(private httpService: HttpService, private userService: UserService, private router: Router) {}
ngOnInit() {
console.log(this.userService.hasUser());
if(!this.userService.hasUser()) { console.log('navigating'); this.router.navigate(['/login']);}
}
}
This is not working as you might imagine, because of the async nature of Capacitor Preferences. When it tries to check for the user it still has not yet finished looking for it. So, what could be a better and correct way to do this?
CodePudding user response:
To avoid the issue of timing, you can have your service return a Promise
or Observable
, so that consumers are notified when the async value has been received.
Then, you can implement a Route Guard (CanActivate) to redirect to the login page when the user has not been set. When a route guard is used, it's not necessary to put the redirect logic in your components, you simply add the guard to the route definition.
- Return promise in service:
export class UserService {
private user: Promise<User|undefined> = Preferences.get({ key: 'user' }).then(
data => data ? JSON.parse(data.value) : undefined
);
hasUser(): Promise<boolean> {
return this.user.then(user => !!user);
}
}
- Implement route guard:
export class AuthGuard implements CanActivate {
constructor(userService: UserService, private router: Router) { }
async canActivate() {
return await this.userService.hasUser()
? true
: router.parseUrl('/login');
}
}
- Protect necessary routes:
const routes: Routes = [
{ path: 'home', component: HomeComponent, canActivate: [AuthGuard] },
];