LiveLoveApp logo

Solution - AsyncSubject

Solution

import { AsyncSubject, fromEvent } from 'rxjs';
import { first, tap } from 'rxjs/operators';

const body = document.querySelector('body')!;
const clicks = fromEvent(body, 'mousedown').pipe(
  tap((event: MouseEvent) => console.log(event.pageX, event.pageY))
);

/**
 * 1. Create a new AsyncSubject and specify the generic type
 *    `MouseEvent` for the next notification..
 */
const asyncSubject = new AsyncSubject<MouseEvent>();

/**
 * 2. Subscribe to the `AsyncSubject` and set the `x` and `y`
 *    input values to the `pageX` and `pageY` values from the
 *    `MouseEvent` object.
 */
const x = document.querySelector('#x') as HTMLInputElement;
const y = document.querySelector('#y') as HTMLInputElement;
asyncSubject.subscribe((event) => {
  x.value = event.pageX.toString();
  y.value = event.pageY.toString();
});

/**
 * 3. Subscribee to the `clicks` observable using the `AsyncSubject`
 *    instance as the Observer.
 */
clicks.subscribe(asyncSubject);

/**
 * 4. Complete the `AsyncSubject` when the button is clicked.
 */
const btn = document.querySelector('#btn') as HTMLButtonElement;
fromEvent(btn, 'click')
  .pipe(
    first(),
    tap(() => {
      // complete AsyncSubject
      asyncSubject.complete();
    })
  )
  .subscribe();

Let's review the solution code above:

  • First we do a bit of setup. We get a reference to the HTMLBodyElement in the DOM, and we use the fromEvent() operator to add an event listener to the body element's mousedown events. Using the tap() operator we log out the pageX and pageY values as a side effect.
  • Next, create a new AsyncSubject and specify the generic type of MouseEvent.
  • We then subscribe to the AsyncSubject instance and set the x and y input element value properties to the pageX and pageY values from the event object.
  • We subscribe to the clicks Observable and set the asyncSubject as the Observer.
  • Finally, we use the fromEvent() operator to add an event listener to the btn element. Using the first() operator we will unsubscribe after the first next notification, and then using the tap() operator we invoke the complete() method on the AsyncSubject. Again, note that if an AsyncSubject never completes then the Observers will never receive a next notification.