I ave the following issue in Angular:
A. A user service subscribes to an oAuth provider and on next, it sets a property called activeUserId to the value received from the observable.
B. 5 components have an onClick() method which call backendServices. These services accept an input parameter "activeUserId" of type number.
What is the best way of making sure the "onClick" is only called when the activeUserId is actually already set, or can an observable of the user service be passed into the other services instead of number? I would like to not have to subscribe to the user service in each component - making sure the user can click an element to call the service even if it is 100ms before the user service observable has retrieved a value.
Thank you Thomas
CodePudding user response:
What I believe you want to do is validate a user's authentication pre route load. This way, you can always be sure that the user's id is available for use. One way to do it is to use a Route Guard
. This will ensure user authentication pre route load.
In this example, I store user data in local storage so that when a user refreshes the page or accidentally clicks out, their authentication is based on it's token expiration from the user object in storage vs. in-memory.
If the user is validated, you can safely call upon the user's in-memory value without subscribing.
auth.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { StorageService } from '../services/storage.service';
import { UserService } from '../services/user.service';
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(
private router: Router,
private userService: UserService,
private storageService: StorageService
) { }
canActivate(
): Promise<boolean> | boolean {
return new Promise((resolve) => {
const userStore = this.userService.user$.value
if (!userStore.id) {
const userStorage = this.storageService.get('user')
if (!userStorage) {
this.router.navigate(['login'])
resolve(true)
} else {
this.userService.user$.next(userStorage)
resolve(true)
}
} else {
resolve(true)
}
})
}
}
main.routes.ts
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [AuthGuard],
}
dashboard.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.style.scss'],
})
export class DashboardComponent {
constructor(private userService: UserService) {
const user = this.userService.user$.value
console.log('user should be active and validated here', user)
}
}
CodePudding user response:
I can think of 2 simple strategies, of course there are many options.
- Make the
onClick
call invoke a facade service class which has the user service injected to it. - Make the
activeUserId
aBehaviorSubject
and subscribe to changes. You can make the button enabled once you receive a valid value from subscription for example. You can do something similar with the first strategy too.
I think the first strategy is better, as you can tweak your facade service however you like.