收听Observable.timer并发出父组件

时间:2018-01-24 21:23:50

标签: angular timer observable countdown eventemitter

我有一个使用Observable.timer的倒计时组件,它在父组件中使用。使用它时,从父组件中获取@Input() seconds: string,用作倒计时ex: 60开始的数字。现在,我想向父组件发出倒计时数@Output() checkTime,以便在按钮到达0时禁用该按钮。

所以我将倒计时编号订阅到checkTime事件发射器。但结果是,倒计时不会将其停在0并且它会不断递减。任何人都可以看到下面的问题吗?

register.component.html

<tr *ngFor="let course of courses">            
    <td>
        <button class="btn btn-info" [disabled]="counter === 0"> Register</button>              
    </td>  
    <td>
        <countdown [seconds]="60" (checkTime)="checkTimeExpired($event);"></countdown>
    </td>         
</tr>   

countdown.component.ts

import { Component, Input, Output, OnInit, EventEmitter } from '@angular/core'
import { Observable, Subscription } from 'rxjs/Rx';

@Component({
  selector: 'countdown',
  template: '{{ countDown | async | formatTime }}'
})
export class CountdownComponent implements OnInit {
  @Input() seconds: string;
  @Output() checkTime: EventEmitter<number> = new EventEmitter();
  countDown: any;
  counter: number;
  private subscription: Subscription;

  constructor() {}

  ngOnInit() {
    this.counter = parseInt(this.seconds, 10);
    this.countDown = Observable.timer(0, 1000)
                    .take(this.counter)
                    .map(() => --this.counter)

    this.subscription = this.countDown.subscribe(t => {
        this.checkTime.emit(this.counter)
    });                   
  }
}

register.component.ts

counter: number;

checkTimeExpired(evt) {
   this.counter = evt;   
}

如果我不使用订阅,倒计时将停在0。

this.subscription = this.countDown.subscribe(t => {
    this.checkTime.emit(this.counter)
}); 

很抱歉,如果已经有相关问题,请帮助这个新输入的角色用户。

2 个答案:

答案 0 :(得分:2)

主要问题是你将Observable的值与任意变量分离。通常你想要一个Observable流来为你转换一个值 - 这是RxJS的一半。

问题在于.map(() => --this.counter)map运算符转换值流并返回新值。在这里,您将Observable值转换为--counter;有问题。 --counter将返回counter - 1并递减counter,但该值与Observable流完全解耦。这是什么意思?

从该Observable外部控制该值会使Observable hot 。它的值可以流向不同的订阅者,但值可能会因外部因素而发生变化。 (请记住,Observable在订阅之前不会执行)

但是......你只有一个用户,所以这不是问题,对吧?实际上,Angular async pipe 订阅了Observables。这意味着您的Observable已经订阅两次,这意味着有两个定时器倒计时,这意味着有两个流执行map函数 - 这意味着每秒,{{1}正在递减两次

我建议将Observable的计时器值和实际时间值组合在一起,然后仅将您的事件作为副作用发出:

this.counter

ngOnInit() { const start = parseInt(this.seconds, 10); this.countDown = Observable.timer(0, 1000) .take(start + 1) // add one, so zero is included in emissions. .map(i => start - i) // decrement the stream's value and return .do(s => this.checkTime.emit(s)) // do a side effect without affecting value } 管道将订阅它并开始排放。最好的部分是,async管道会在销毁时自动清理订阅。

另一个想法是使用async,这是一种更具说明性的方式来说明应该采取多少排放,并在提供的表达式为假时完成:

takeWhile

希望有助于消除一些困惑!

答案 1 :(得分:-1)

您可以尝试停止订阅倒计时,计时器将在后台运行。

Stackblitz Rebuild