ryanmcfall
ryanmcfall

Reputation: 617

Binding to the same observable twice in the same Angular template using async

I have the following inside of a Component's template:

<span>
  {{(userService.CurrentUser | async)?.FullName }}
</span>

userService.CurrentUser is an Observable that generates objects of type User. User has propreties named FirstName, LastName, FullName, etc.

This code works fine as it stands. I'm now trying to customize what is displayed when at different screen sizes. I've modified the template as follows:

<!--  At screen size sm , show the user's first name  -->
<span class="d-none d-sm-inline d-md-none">
  {{(userService.CurrentUser | async)?.FirstName }}
</span>

<!--  At screen size md or larger, display user's full name  -->
<span class="d-none d-md-inline">
  {{(userService.CurrentUser | async)?.FullName }}
</span>

Unfortunately, this doesn't work as expected. A little experimentation seems to indicate that whichever one of the two <span> elements comes first in the template's markup will have its expression evaluated, while the second one won't, leading it to have no content.

If I bind to a property within the component instead of using the async pipe to bind to an Observable, I don't have this problem.

While I could keep track of the current value of the Observable by subscribing to it within my component and maintaining my own copy of the most recent value of the Observable, this seems to be rewriting the async pipe.

I also see in angular - using async pipe on observable<Object> and bind it to local variable in html that I could get the most recent observable value stored into a variable by using an *ngIf.

Why does what I have not work as expected? I'd like to understand before going with one of the two alternatives above.

Update: Here's the code I'm using the set up the CurrentUser Observable. Based on a comment, I think this is the source of the problem. I suspect that it's the fact that I'm only keeping track of a single subscriber in the function passed to the Observable constructor. I don't think I understand the proper way to create an Observable and then notify Observers.

export class UserService {
  private currentUser : User;
  private currentUserObservable : Observable<User>;
  private currentUserObserver : Observer<User>;

  constructor()  {
    this.currentUserObservable = new Observable<User>(
      observer => {
        this.currentUserObserver = observer;
      }
    );
  }

  get CurrentUser() : Observable<User> {
    return this.currentUserObservable;
  }

  login (emailAddress : string, password : string) : void {
    this.currentUser = new User(emailAddress, "username", "First Name", "Last Name");      
    this.currentUserObserver.next(this.currentUser);
  }
}

Upvotes: 3

Views: 1204

Answers (1)

Alex Morente
Alex Morente

Reputation: 31

It should work as it is. However there are several inefficiencies as the documentation points out: Storing conditional result in a variable (Show a set of properties from the same object)

You could try:

<ng-container *ngIf="userService.CurrentUser | async as user">

  <!--  At screen size sm , show the user's first name  -->
  <span class="d-none d-sm-inline d-md-none">
    {{ user.FirstName }}
  </span>

  <!--  At screen size md or larger, display user's full name  -->
  <span class="d-none d-md-inline">
    {{ user.FullName }}
  </span>

</ng-container>

There is only one suscription and it is much cleaner.

Upvotes: 3

Related Questions