Search code examples
angulartypescriptdeclarative

Declarative approach for subscribing to and receiving changes between unrelated components in Angular?


Problem statement:

  • I have a Dashboard (parent component) which have Sidebar & Navbar (child component).
  • In Sidebar, on links I have custom permission directive applied which checks whether the scope have permission to view or not.
  • In Navbar, I have a menu with Admin, Manager & staff roles options & when user changes the roles
  • I want to change the sidebar links based on that changed scope permissions.

I tried passing observable to sidebar

scope$: Observable<Roles> = this.scopeService.scope$;

using aysnc pipe

<app-sidebar [scope]="scope$ | async"></app-sidebar>

& subscribing in ngOnInit

ngOnInit(): void {
    this.scopeService.scope$.subscribe();
  }

but it doesn't work.

I have seen post which use the event emitter but I really want to use declarative pattern.

Here is the stackblitz link for more brief.


Solution

  • That's not how async pipe works. Async pipe automatically subscribes to observable, and gets its value. In your case you don't want to do it, you just want to pass observable. You don't need async pipe for that, just do this:

    <app-sidebar [scope$]="scope$"></app-sidebar>
    

    and now in app-sidebar:

    @Input
    scope$: Observable<Roles> = this.scopeService.scope$;
    
    ngOnInit(): void {
        this.scope$.subscribe();
      }
    

    EDIT:

    After checking your stackblitz I see where your problem is. In your has-permission directive you use snapshot of scope instead of subscribing to it. You should do this:

      ngOnInit(): void {
        this.scopeService.scope$.subscribe((newScope) => {
          this.scope = newScope;
          this.checkPermissions();
        });
      }
    

    This way, every time scope changes, checkPermissions() method is called. You still need to fix how you show your elements, because there are some issues with it. Seems that you're not clearing options that scole already had permissions for, and you create a new one, making it duplicate. YOu could fix it with making showElement like this:

      showElement(canShow: boolean): void {
        this.viewContainer.clear();
        if (canShow) {
          this.viewContainer.createEmbeddedView(this.templateRef);
        }
      }