Home > Software engineering >  How to switch an RxJS Oberservable<boolean> from true to false and vice versa
How to switch an RxJS Oberservable<boolean> from true to false and vice versa

Time:09-29

I'm learning Angular and I have a pretty simple question : I have an Observable that I use to track if a side menu is closed or opened. I get to have my Observable to start with 'true' as a value, but I really don't get how I can swap it so it does this logic : value = !value

Here's my service :

import { Injectable } from '@angular/core';
import { Observable, startWith } from "rxjs";

@Injectable({
  providedIn: 'root'
})
export class SideMenuService {
  private open$ = new Observable<boolean>().pipe(
    startWith<boolean>(true)
  );

  constructor() { }

  getOpen() {
    return this.open$;
  }

  swapOpen() {
    // Here I want to make open$ to be false
  }
}

And here's my Typescript file :

import { Component, OnInit } from '@angular/core';
import { SideMenuService } from "../../../services/side-menu/side-menu.service";

@Component({
  selector: 'side-menu',
  templateUrl: './side-menu.component.html',
  styleUrls: ['./side-menu.component.css']
})
export class SideMenuComponent implements OnInit {

  public open!: boolean;

  constructor(
    private sideMenuService: SideMenuService
  ) { }

  ngOnInit(): void {
    this.sideMenuService.getOpen()
      .subscribe(result => this.open = result);
  }

  swtichTriggered(): void {
    this.sideMenuService.swapOpen();
  }
}

How can I toggle the value properly in the swapOpen() function?

CodePudding user response:

Some RxJS operator magic via scan

(On mobile so forgive the formatting)

toggle$ = new Subject<void>()

// either subscribe or better yet use async pipe in component
isOpen$ = toggle$.pipe(
    // start with true then switch on toggle emission 
    scan((acc, _) => !acc, true)
)

toggle$.next() // triggers isOpen$ i.e. use in swapOpen

// don’t forget to clean up subscriptions 

CodePudding user response:

You can create a BehaviourSubject and toggle its value whenever the sidebar opens or closes

import { Injectable } from '@angular/core';
import { Observable, startWith } from "rxjs";

@Injectable({
  providedIn: 'root'
})
export class SideMenuService {
  private open$ = new BehaviourSubject<boolean>(true);

  constructor() { }

  getOpen() {
    return this.open$;
  }

  swapOpen(value: boolean) {
    this.open$.next(value);
  }
}

In your component where you are looking at the component

import { Component, OnInit } from '@angular/core';
import { SideMenuService } from "../../../services/side-menu/side-menu.service";

@Component({
  selector: 'side-menu',
  templateUrl: './side-menu.component.html',
  styleUrls: ['./side-menu.component.css']
})
export class SideMenuComponent implements OnInit {

  public open!: boolean;

  constructor(
    private sideMenuService: SideMenuService
  ) { }

  ngOnInit(): void {
    this.sideMenuService.getOpen()
      .subscribe(result => this.open = result);
  }

  swtichTriggered(): void {
    this.sideMenuService.swapOpen(!this.open);
  }
}

One of the benefits of using a Subject over an Observable would be that it is multicast so only a single instance of the Subject will be used to pass value to its Observers.

The same behavior can be achieved in the Observable by using share operator which basically uses a Subject underneath its implementation.

  • Related