Home > Blockchain >  Angular doesn't write the value after updating sessionStorage
Angular doesn't write the value after updating sessionStorage

Time:01-08

My code is pretty simple, I have this html in my component

<h2>{{username}}</h2>
<h2>{{sessionId}}</h2>

An this is the script I use to update those variables


    //i check for when the router redirects to the /user component
    router.events.pipe(
      filter((e: Event): e is RouterEvent => e instanceof RouterEvent)
    ).subscribe(() => {
      //router event happens
      if (location.path() == "/user") {
        //I do a call to the backend to get the user info I need
        let userTemp = <string>sessionStorage.getItem("username");
        const params = new HttpParams()
          .set('username', userTemp);
        //the call happens in the service calss that I inject
        this.service.getUserInfo(params);
        //all it does is getting the data and sessionStorage.setItem() on both values
        this.username = <string>sessionStorage.getItem("username");
        this.sessionId = <string>sessionStorage.getItem("sessionId");
      }
    });

Even tho I set the values first and then get them, only the username variable is shown in the html, the sessionId variable doesn't get drawn between the <h2></h2> tags. This could be because I already have the "username" value in the sessionStorage. Note: both variables are set as 'null' in the class

This is the service:

getUserInfo(params: HttpParams) {
    this.http.get<UserData>('/api/getuserinfo', { params, responseType: 'json' }).subscribe(response => {
      //I set the username again
      sessionStorage.setItem("username", response.username);
      //I add a new value called sessionId
      sessionStorage.setItem("sessionId", response.sessionId);
    });
  }

I tried setting the ngOnInit() function to set the values on initialization and then call this.ngOnInit() after the service call but even if the function gets called. Any varioation on the code has not helped , and I didn't find anything that could explain why the variable update is not seen by Angular, even though from the debugger I can access the value in the sessionStorage object.

CodePudding user response:

The is you have is because of the timing of when each function is executed by the Javascript engine.

Looking into your code, you have an observable, ´router.events`, that within its subscription performs several things. Until here everything is fine. However it is within the subscribe the that issue occurs, more precisely, when the call to your service is performed.

subscribe(() => {
  if (location.path() == "/user") {
    let userTemp = <string>sessionStorage.getItem("username");
    const params = new HttpParams().set('username', userTemp);
    
    this.service.getUserInfo(params); // <- Here is the issue.
    
    this.username = <string>sessionStorage.getItem("username");
    this.sessionId = <string>sessionStorage.getItem("sessionId");
  }
})

When javascript executes this code, the order is the following:

  1. Enters the if block;
  2. Tries to get the "username" from the sessionStorage and assigns it to the userTemp.
  3. Creates the params HttpParams instance and sets the value of provided property.
  4. Calls the getUserInfo(params). This method creates and observable and subscribes to it.
  5. Tries to get the "username" from the sessionStorage and assigns it to the this.username.
  6. Tries to get the "sessionId" from the sessionStorage and assigns it to the sessionId.
  7. Resolves the code within the subscribe of the getUserInfo(params), where it sets the values in the sessionStorage.

As you can see, the issue is that you want for the step 7 to be executed before the step 5 and 6.

For this you either need to rely on observable operators (which is more complex) or you need to convert the return from the this.service.getUserInfo(params) into a Promise and await its resolution.

If you want to convert it to a Promise, the easiest way is the following:

getUserInfo(params: HttpParams): Promise<void> {
  this.http.get<UserData>('/api/getuserinfo', { params, responseType: 'json' })
    .pipe(tap(response => {
      sessionStorage.setItem("username", response.username);
      sessionStorage.setItem("sessionId", response.sessionId);
    })).toPromise();
}

In your subscribe you also need to modify it to make asynchronous, like so:

subscribe(async () => {  // Add async here
  if (location.path() == "/user") {
    let userTemp = <string>sessionStorage.getItem("username");
    const params = new HttpParams().set('username', userTemp);
    
    await this.service.getUserInfo(params); // Await the resolution of the api call
    
    this.username = <string>sessionStorage.getItem("username");
    this.sessionId = <string>sessionStorage.getItem("sessionId");
  }
})

CodePudding user response:

The problem is that you get session keys before you even set them. Actually, when you subscribe on stream, the code will not stop tracing until the response comes. All lines after the subscription immediately run. If you want to get data from session, you should put the code inside the subscription. So you should do exactly what Giovanni Grana said in his answer.

CodePudding user response:

Here's how I resolved it without using a promise. I return the http.get in the service like this:

getUserInfo(params: HttpParams) {
return this.http.get<UserData>('/api/getuserinfo', { params, responseType: 'json' })
  .pipe(tap(response => {
    sessionStorage.setItem("username", response.username);
    sessionStorage.setItem("sessionId", response.sessionId);
  }));
}

And subscribe to the service.getUserInfo(params) in the component where I use the service:

this.service.getUserInfo(params).subscribe(() => {
      this.update();
    });

update is a function that just updates the variables:

update() {
    this.username = <string>sessionStorage.getItem("username");
    this.sessionId = <string>sessionStorage.getItem("sessionId");
}

By doing this I force the update to happen after the get has finished.

  • Related