Reputation: 261
I'm using an Auth service with OIDC to get user data after a user is logged in. I need that user data to determine what side-nav items a user will see, which is handled in a StepsService.
My current approach is creating an observable User that the StepService can reach out to, and use the data from that Observable to set all of the necessary StepService values. My files are as follows:
steps.service.ts
export class StepsService {
// Instantiate the initial variables that will act as state storage
sections: Observable<SectionModel[]>;
currentSection: Observable<SectionModel>;
stepsInCurrentSection: Observable<StepModel[]>;
currentStep: Observable<StepModel>;
currentStepIndex: BehaviorSubject<number> = new BehaviorSubject<number>(0);
totalStepsInCurrentSection: number;
totalSections: number;
currentSectionIndex: BehaviorSubject<number> = new BehaviorSubject<number>(0);
numberOfCompletedSteps: BehaviorSubject<number> = new BehaviorSubject<number>(0);
constructor(private authService: AuthService) {
// Set state variables based on initial steps available
this.sections = this.authService.getUser().pipe(
map((user) => {
console.log('user is: ', user);
const userRoles: string[] = user.profile['role'];
return this.getUserSpecificSteps(userRoles);
}),
tap((sections) => (this.totalSections = sections.length))
);
this.currentSection = combineLatest([this.sections, this.currentSectionIndex]).pipe(
map((data) => {
const sections = data[0];
const index = data[1];
return sections[index];
})
);
this.stepsInCurrentSection = this.currentSection.pipe(
map((sections) => sections.steps),
tap((steps) => (this.totalStepsInCurrentSection = steps.length))
);
this.currentStep = combineLatest([this.stepsInCurrentSection, this.currentStepIndex]).pipe(
map((data) => {
const steps = data[0];
const index = data[1];
return steps[index];
})
);
}
getUserSpecificSteps(userRoles: string[]) {
return userRoles?.includes('Admin') ? adminSections : userSections;
}
...
auth.service.ts
export class AuthService {
private config = environment;
private _isAuthenticated: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public readonly IsAuthenticated: Observable<boolean> = this._isAuthenticated.asObservable();
private _userManager: UserManager;
public _user: User | null;
public userSubject$: Observable<User>;
constructor() {
this._userManager = new UserManager(getClientSettings(this.config));
this._userManager
.getUser()
.then((user: any) => {
if (user && !user.expired) {
this._user = user;
this.userSubject$ = user;
this._isAuthenticated.next(this.isAuthenticated());
}
return user;
})
.catch((e) => console.log(e));
//fires after every user refresh
this._userManager.events.addUserLoaded((args) => {
this._userManager
.getUser()
.then((user) => {
this._user = user;
})
.catch((e) => console.log(e));
});
}
getUser() {
return this.userSubject$;
}
...
Currently this throws an error that this.authService.getUser() is undefined
and if I add a null check after the method call, it throws an error that I am passing undefined data to a data stream.
I do also have an APP_INITIALIZER
inside of my app.module
file that initializes the app with the auth service. I'm not sure if I'm using the observables correctly, or what the problem could be.
Thanks in advance!
Upvotes: 0
Views: 136
Reputation: 1633
You should probably use something like eslint.
this._userManager
.getUser()
.then((user: any) => { <- remove : any it will not tell you what type you have
if (user && !user.expired) {
this._user = user; <- is it user or is it a subject<user>?
this.userSubject$ = user; <-is it user or is it a subject<user>?
this._isAuthenticated.next(this.isAuthenticated());
}
return user;
})
my assumption you a are getting authService.getUser() is undefined is due to the subject. I would just set it as behavior subject since you add data to it via a differnet service so.
public userSubject$ = new BehaviorSubject<User| null>(null);
this means you now getUser() is never undefined. however the value might be null
setting the value:
this._userManager
.getUser()
.then((user: User) => {
if (user && !user.expired) {
this._user = user;
this.userSubject$.next( user); <- assuming that user is type User
this._isAuthenticated.next(this.isAuthenticated());
}
return user;
})
Upvotes: 1