I have a simple component with a single button that starts and pauses a stream of numbers generated by RxJS timer.
import { Component, OnInit } from \'@angul
I'm assuming that the desired behaviour is not related to getting the values that the timer emits per se, and that instead of pausing notifications to an ongoing stream (in your example, the timer continues even if we don't see the values being printed), it's okay to actually stop emitting when paused.
My solution is inspired by the Stopwatch recipe
The solution below uses two separate buttons for play and pause, but you can adjust this to taste. We pass the (ViewChild) buttons to the service in the ngAfterViewInit hook of the component, then we subscribe to the stream.
// pausable.component.ts
ngAfterViewInit() {
this.pausableService.initPausableStream(this.start.nativeElement, this.pause.nativeElement);
this.pausableService.counter$
.pipe(takeUntil(this.unsubscribe$)) // don't forget to unsubscribe :)
.subscribe((state: State) => {
console.log(state.value); // whatever you need
});
}
// pausable.service.ts
import { Injectable } from '@angular/core';
import { merge, fromEvent, Subject, interval, NEVER } from 'rxjs';
import { mapTo, startWith, scan, switchMap, tap, map } from 'rxjs/operators';
export interface State {
active: boolean;
value: number;
}
@Injectable({
providedIn: 'root'
})
export class PausableService {
public counter$;
constructor() { }
initPausableStream(start: HTMLElement, pause: HTMLElement) {
// convenience functions to map an element click to a result
const fromClick = (el: HTMLElement) => fromEvent(el, 'click');
const clickMapTo = (el: HTMLElement, obj: {}) => fromClick(el).pipe(mapTo(obj));
const pauseByCondition$ = new Subject();
const pauseCondition = (state: State): boolean => state.value % 5 === 0 && state.value !== 0;
// define the events that may trigger a change
const events$ = merge(
clickMapTo(start, { active: true }),
clickMapTo(pause, { active: false }),
pauseByCondition$.pipe(mapTo({ active: false }))
);
// switch the counter stream based on events
this.counter$ = events$.pipe(
startWith({ active: true, value: 0 }),
scan((state: State, curr) => ({ ...state, ...curr }), {}),
switchMap((state: State) => state.active
? interval(500).pipe(
tap(_ => ++state.value),
map(_ => state))
: NEVER),
tap((state: State) => {
if (pauseCondition(state)) {
pauseByCondition$.next(); // trigger pause
}
})
);
}
}