user20565161
user20565161

Reputation: 3

Angular local storage value being read before set

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

Upvotes: 0

Views: 1077

Answers (1)

Harun Yilmaz
Harun Yilmaz

Reputation: 8558

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

Upvotes: 1

Related Questions