I have a header component which makes a call to fetch the user token and store the user's data. I then have a different component (admin component) that is not a child of the header which relies on the user data to fetch additional data. The issue I'm running into is that the admin component initializes before the header component has finished making it's call for the user, therefore breaking my admin component. In other words, the code execution breaks because the active company has not been set yet by the header. Is there a way to make sure these load synchronously in Angular 6 so that I can avoid this issue?
header.component.ts
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../../services/auth.service';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
user: any;
activeCompany: any;
constructor(private authService: AuthService) { }
ngOnInit() {
this.authService.getToken().then((user) => {
this.user = user;
this.user.Companies = JSON.parse(this.user.Companies.split('"').join('"'));
this.authService.getActiveCompany$().subscribe((company: any) => {
if(!company) {
let company = this.user.Companies[0]
this.authService.setActiveCompany$(JSON.stringify(company));
}
this.activeCompany = company;
});
});
}
setActiveCompany(company) {
this.authService.setActiveCompany$(company)
}
}
admin.component.ts
import { Component, OnInit } from '@angular/core';
import { TagService } from '../../services/tag.service';
import { AuthService } from '../../services/auth.service';
@Component({
selector: 'app-admin',
templateUrl: './admin.component.html',
styleUrls: ['./admin.component.css']
})
export class AdminComponent implements OnInit {
companyId: number;
tags: any;
loading: boolean = true;
constructor(
private tagService: TagService,
private authService: AuthService
) {}
ngOnInit() {
this.authService.getActiveCompany$().subscribe((company: any) => {
// The line below breaks because the active company has not been set yet by the header
this.companyId = company.Id
this.tagService.getTags(companyId).then((tags) => {
this.setTags(tags)
this.loading = false;
});
});
}
}
auth.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, Subject, BehaviorSubject } from 'rxjs';
import { AppConfig } from '../../app-config';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private activeCompany$: Subject<any>;
constructor(private http: HttpClient, private _config: AppConfig) {
let initialActiveCompany;
if (window.localStorage.getItem('activeCompany')) {
initialActiveCompany = JSON.parse(window.localStorage.getItem('activeCompany'));
} else {
this.getToken().then((user: any) => {
initialActiveCompany = user.Companies = JSON.parse(user.Companies.split('"').join('"'))[0];
});
}
this.activeCompany$ = new BehaviorSubject<any>(initialActiveCompany);
}
getToken() {
return new Promise(resolve => {
this.http.get(`${this._config.API_URL}/Token`).subscribe(data => {
resolve(data);},
err => {
console.log("Error retrieving token", err);
});
});
}
// Returns the observable (read-only) part of this subject
getActiveCompany$(): Observable<any> {
return this.activeCompany$.asObservable();
}
// Stores the new company value in local storage and pushes it to the subject
setActiveCompany$(company: any) {
window.localStorage.setItem('activeCompany', JSON.stringify(company));
this.activeCompany$.next(company);
}
}
I think you should organize your services little bit more for handling asynchronicity.
You should make the activeCompany
as BehaviorSubject
in the AuthService, and then subscribe for change in admin.
Because you need to call the getTags()
whenever the activeCompany
is changed.
I'd advise that you use a resolver service in your admin component. First create a new resolver service using ng g s resolver command in your cli then use this code
import { Injectable } from "@angular/core";
import { Observable } from "rxjs";
import { AuthService } from '../../services/auth.service';
import {
Resolve,
ActivatedRouteSnapshot,
RouterStateSnapshot
} from "@angular/router";
@Injectable({
providedIn: "root"
})
export class ResolverService implements Resolve<any> {
this.companyId: any;
constructor(private authService: AuthService) {}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<any> {
return this.authService.getActiveCompany$().subscribe((company: any) => {
this.companyId = company.Id
this.tagService.getTags(companyId).then((tags) => {
this.setTags(tags)
this.loading = false;
});
});
}
}
and then add this to your routes-module under your admin component
{
path: "admin",
component: AdminComponent,
resolve: {
result: ResolverService
}
}
and then finally in your admin component import ActicatedRoute
import { ActivatedRoute } from "@angular/router";
and add to your constructor
userData = null // store the data you want from your authService here from your resolver
constructor(private actr: ActivatedRoute) {
this.actr.data.pipe(map(data => data.resuslt)).subscribe(res => {
this.userData = res;
});
}
I didn't have time to read through your authservice.ts but i hope you get the concept
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With