Why behavioral is always better?

So, recently I was kinda fighting with really confusing behavior of subject component-component notification. Consider following template, where some non-observable variable (displayNiceThings) conditionally display some underlying observable-dependent stuff (conditionally displayed set of buttons).

In example, we can switch static text that shall be displayed on buttons, while the functionality of buttons will be the same. Kind of a button depends on asynchronous context of observable, that is emitted outside of component by service that manages authorization. So far, so good.

<div class="buttons" *ngIf="displayNiceThings">

        <button *ngIf="(loggedState$ | async) === false" (click)="logIn()">
          Log me In please
          
        </button>


        <button *ngIf="(loggedState$ | async) === true" (click="logOut()">
          Log me Out please
        
        </button>
</div>




<div class="buttons" *ngIf="!displayNiceThings">
        <button *ngIf="(loggedState$ | async) === true" (click)="logIn()">
          Log me Out damn!
        </button>


        <button *ngIf="(loggedState$ | async) === true" (click)="logOut()">
          Log me Out right now!
        </button>

</div>

        
<button (click)="switchPoliteness()">
          Rude mode / Polite mode
        
</button>

Then, in controller, on politeness switch we:

  • change value of displayNiceThings
  • listen and observe on authorization service log in/out state changes
  • provide some abstracted away log-in/log-out functionality
loggedState$: Subject<boolean> = new Subject();

switchPoliteness() {
  this.displayNiceThings = !this.displayNiceThings;
}

ngOnInit() {
  this.displayNiceThings  = true;
  this.someAuthService.getLoggedInStateObservable();
  .subscribe(v => {
    this.loggedState$.next(v);
  })
}

logOut() {
  // Not relevant
}

logIn() {
  // Not relevant
}

Let's assume, that authentication/authorization service is delivering logged in value at start, then we ends up with fully configured component: at init we grab static value of politeness and subscribe to auth service and re-write it's values to our own internal subject (wrapping up stuff with internal component management unit is kinda good practice I can recommend)

Unfortunately, this will fail in misery when we decide, to switch politeness mode. Our button will be gone forever (or at least until somebody will actually change his logging state so auth service will re-emitt event)

Why does it works so bad? Async pipe is resolution to many issues, but unfortunately sometimes it might create more of them. In our current case, ngIf will be resolved only after async pipe input will provide any value. Until auth service remain silent about changes, async pipe will just subscribe to it and wait. Thus, unable to decide, which version of template shall be displayed, we end up with nothing.

How to overcome?

Actually, when you figure out what's wrong, fixing it is kinda easy. Fair enough is to replace internal subject wrapping auth service response with behavioral subject, that basically is subject but with memory of last emitted value. Because we don't really know, how to initialize it, we can move out from eager initialization to kind of lazy-init at demand:

loggedState$: BehaviorSubject<boolean>;

switchPoliteness() {
  this.displayNiceThings = !this.displayNiceThings;
}

ngOnInit() {
  this.displayNiceThings  = true;
  this.someAuthService.getLoggedInStateObservable();
  .subscribe(v => {
    if (this.loggedState$) {
      this.loggedState$.next(v);
    } else {
      this.loggedState$ = new BehaviorSubject(v)
    }
    
  })
}

logOut() {
  // Not relevant
}

logIn() {
  // Not relevant
}

Benefits are, that when politeness level is switched, new set of ngIf buttins will be turned alive. They will launch their async pipes, but now, because subject is behavioral and remembers last value, is able to instantly re-emit this value at each subscription. Both async pipes will be feeded with value of most recent log in/out state.



要查看或添加评论,请登录

Tomasz Budziński的更多文章

社区洞察

其他会员也浏览了