I have a random number between 1 and 20 at intervals. I need to add buttons to move up and down. (increment & decrement). I could not combine the intervals I have with my functions
my stackblitz: https://stackblitz.com/edit/angular-ivy-zbrapb?file=src/app/app.component.ts,src/app/app.component.html,src/app/app.component.css
The function that does a random number between 1 and 20:
floor!: Observable<Number>;
floorNumber = -1;
ngOnInit(): void {
this.floor = this.getNumbersInfinite();
}
getNumbersInfinite() {
return interval(1000)
.pipe(
map(() => Math.floor(Math.random() * 20) 1),
tap(res =>this.floorNumber=res));
}
Unsuccessful implement of up and down:
up(){
if (Number(this.floorNumber) < 20 && Number(this.floorNumber) >= 1) {
return Number(this.floorNumber );
}
return false;
}
down(){
if (Number(this.floorNumber) < 20 && Number(this.floorNumber) >= 1) {
return Number(this.floorNumber--);
}
return false;
}
html:
<p>
{{ floor | async }}
</p>
<input type="button" value="Up" (click)="up()" />
<input type="button" value="down" (click)="down()" />
CodePudding user response:
This should do the trick
export class AppComponent implements OnInit {
floor = new ReplaySubject<number>(1);
ngOnInit(): void {
this.getNumbersInfinite();
}
getNumbersInfinite() {
return interval(1000)
.pipe(map(() => Math.floor(Math.random() * 20) 1))
.subscribe(this.floor);
}
up() {
this.floor
.pipe(
take(1),
filter((v) => v < 20),
map((v) => v 1)
)
.subscribe((v) => this.floor.next(v));
}
down() {
this.floor
.pipe(
take(1),
filter((v) => v > 1),
map((v) => v - 1)
)
.subscribe((v) => this.floor.next(v));
}
}
CodePudding user response:
You can use the merge()
function to merge the three actions into one observable. Everytime an event happens from one of the observables the value will get sent to the template.
The new template is pretty much the same, we just now remove the click event and create it within the TypeScript using fromEvent()
.
<p>
{{ output$ | async }}
</p>
<input type="button" value="Up" #up />
<input type="button" value="down" #down />
The TypeScript will:
- Create the up event
- Create the down event
- Create the interval
- listen to all three and emit a single observable
export class AppComponent implements AfterViewInit {
// The current value
current = 1;
// Create an Observable from the click event of the up button
up$!: Observable<number>;
@ViewChild('up')
set up(val: ElementRef<HTMLButtonElement>) {
this.up$ = fromEvent(val.nativeElement, 'click').pipe(
filter(() => this.current < 20),
map(() => this.current)
);
}
// Create an Observable from the click event of the down button
down$!: Observable<number>;
@ViewChild('down')
set down(val: ElementRef<HTMLButtonElement>) {
this.down$ = fromEvent(val.nativeElement, 'click').pipe(
filter(() => this.current > 0),
map(() => --this.current)
);
}
// Create a timer observable that fires every "x" milliseconds
interval$ = () =>
timer(0, 1000).pipe(
map(() => Math.floor(Math.random() * 20) 1),
tap((i) => (this.current = i))
);
output$?: Observable<number>;
// We need to merge here so the buttons are ready.
// Merging sooner will cause an `undefined` error for the buttons
ngAfterViewInit() {
// Merge the three observables and send the results to the template
this.output$ = merge(this.up$, this.down$, this.interval$());
}
}