0

Im working with an angular application which has a sidebar component. Depending on the role of the user signed in, there will be more options on the sidebar. For example, a superadmin will have an additional dashboard for him.

The issue is the role is being stored in local storage in the app component's ts while it is being retrived in the nav component ngAfterViewInit. But the code in ngAfterViewInit is being executed first such that it gets a null role.

Here is a code snippet:

App.component.ts

constructor
 (private auth: AuthService)

ngOnInit(){
 this.auth.
 authenticate.subscribe(res 
  =>{ this.storageService.
set('userRole', res.role) })

nav.component.ts

ngAfterViewInit(){
   this.role = this.
   storageService.
   get('userRole')

storageservice.ts

get(key: string){
  const keyVal = string | null = 
  localStorage.getItem(key)

   if(stringValue){
      return JSON.parse(keyVal);
  }

}
  

The value returned is null. I logged it in console n saw that retrieving the value was executed before setting it.

Getting this error:

main.50d4493a21037afa5155.js:1 
    
   ERROR TypeError: Cannot read properties of undefined (reading '0')
at t._next (main.50d4493a21037afa5155.js:1:2189226)
at t.__tryOrUnsub (main.50d4493a21037afa5155.js:1:544147)
at t.next (main.50d4493a21037afa5155.js:1:543316)
at t._next (main.50d4493a21037afa5155.js:1:542366)
at t.next (main.50d4493a21037afa5155.js:1:542037)
at t._next (main.50d4493a21037afa5155.js:1:551777)
at t.next (main.50d4493a21037afa5155.js:1:542037)
at t._subscribe (main.50d4493a21037afa5155.js:1:1526055)
at e._trySubscribe (main.50d4493a21037afa5155.js:1:545724)
at t._trySubscribe (main.50d4493a21037afa5155.js:1:548182)

Im doing a check as posted for role in nav component then assign it to a variable

  • What is the `storageService` doing? Is it another layer above the built-in `localStorage`? If so, you can convert it to Observable approach and in components, you can _watch_ the changes to the local storage. – Harun Yilmaz Nov 21 '22 at 17:25

1 Answers1

1

What you can do is to create a BehaviorSubject in the StorageService that the components/services can subscribe to and get the latest stored value.

Upon each set call, you can update the BehaviorSubject with the latest value, and all the subscribers will be called. You can use map() pipeable operator to filter by key.

And of course, you need to populate the subject on service startup:

export class StorageService{
  private currentStorage = new BehaviorSubject({});

 constructor() {
    this.updateSubject()
 }

  set(key: string, value: any){
    localStorage.setItem(key, value);
    this.updateSubject();
  }


  updateSubject(){
    this.currentStorage.next({...localStorage})
  }

  get(key: string){
    return localStorage.getItem(key)
  }

  watch(key: string){
    return this.currentStorage.pipe(
      map(items => items[key])
    )
  } 
}

In the app.component, you can set the value like before:

ngOnInit(){
   this.auth.authenticate.subscribe(res =>{ this.storageService.
set('userRole', res.role) }
   )
}

And in the nav.component, you can subscribe to it like this:

ngAfterViewInit(){
   this.storageService.watch('userRole').subscribe(role => this.role = role)
}

You can find a working example here: https://stackblitz.com/edit/angular-ivy-ffp2qi?file=src/app/storage.service.ts

Harun Yilmaz
  • 8,281
  • 3
  • 24
  • 35