收听 Observable.timer 并发送给父组件

Listen to Observable.timer and emit to parent component

我有一个使用父组件中使用的 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)
}); 

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

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

Stackblitz Rebuild

主要问题是您将 Observable 的值与任意变量解耦。通常您希望 Observable 流为您转换一个值——这是 RxJS 的一半。

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

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

但是...您只有一个订阅者,所以这不是问题,对吧?实际上,Angular async 管道 订阅了引擎盖下的 Observables。这意味着你的 Observable 已经订阅了 两次,这意味着有两个计时器在倒计时,这意味着有两个流执行那个 map 函数——这意味着每秒,this.counter 被递减 两次 .

我建议结合 Observable 的 timer 值和 actual 时间值,然后仅作为副作用发出您的事件:

  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,这是一种更明确的方式来说明应该采取多少排放量,并在提供的表达式为假时完成:

  ngOnInit() {
    const start = parseInt(this.seconds, 10);
    this.countdown = Observable.timer(0, 1000)
                .map(i => start - i) // decrement the stream's value and return
                .takeWhile(i => i >= 0)
                .do(s => this.checkTime.emit(s)) // do a side effect without affecting value

  }

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