我设法得到了一个工作计时器服务。
我想在代码中重构很多东西,但在这里,我提供了使其与现有应用程序结构一起工作所需的最低分类。
我应用的基本原则是:
-
使用异步订阅
该服务随时间产生值,因此应在组件中作为可观察对象订阅,最好使用
async
管道,以便通过Angular自动清理订阅。
-
缓冲内部可见
使用
Subject
作为计时器$与其消费组件之间的缓冲区。这使得组件始终可以看到有效的可观察对象,甚至在初始化计时器$之前。
-
带有ViewChild的访问按钮
请勿使用
document.getElementById()
因为运行此行时,文档可能尚未准备就绪。使用角度
@ViewChild
而是将元素传递给init上的服务。
这些是我制作的MOD。为了简洁起见,我删去了未更改的块,希望有足够的细节让您进行更改。
PomoTimerService mods
// imports as before, plus
import { tap } from 'rxjs/operators';
@Injectable()
export class PomoTimerService {
timerSource$ = new Subject<any>(); // added '$' to this property, for clarity
// other properties same as before
private buttons; // to receive button references passed in
// Create a new version of init, which is called once and receives the buttons
initTimer (buttons) {
this.buttons = buttons;
this.initTimerParameters();
}
// Renamed the original initTimer() method to initTimerParamters,
// as it is called on true init and also in reset
initTimerParameters() {
// same statements as original initTimer() method
}
startTimer() {
this.timerStarted = true; // moved from component
const interval$: any = interval(1000).pipe(mapTo(-1));
const pause$ = fromEvent(this.buttons.pauseButton.nativeElement, 'click').pipe(mapTo(false));
const resume$ = fromEvent(this.buttons.resumeButton.nativeElement, 'click').pipe(mapTo(true));
const timer$ = merge(pause$, resume$).pipe(
startWith(true), // previously startWith(interval$), but that looks suspect
switchMap(val => (val ? interval$ : empty())),
scan((acc, curr) => (curr ? curr + acc : acc), this.countdownSeconds$),
takeWhile(v => v >= 0),
tap(val => console.log('timeRemaining', val)), // use tap (not subscribe) to monitor on console
tap(val => { // resetting this.timerStarted is a 'side-effect', best done with tap operator rather than finally callback of subscribe
if (val === 0) {
this.timerStarted = false;
}
}),
);
timer$.subscribe(val => this.timerSource$.next(val)) // send values to Subject
}
resetTimer() {
this.initTimerParameters(); // was calling this.initTimer()
}
}
书籍详细信息。ts-模板mods
通过服务的
主题
和
异步
管
将模板变量添加到按钮以在中使用
@查看子对象
属性。
@Component({
selector: 'bc-book-detail',
template: `
<mat-card *ngIf="book">
...
<mat-card-subtitle>Original {{ timerService.timerSource$ | async }}
</mat-card-subtitle>
...
<button #resume id="resume" ...</button>
<button #pause id="pause" ...</button>
<button #reset id="reset" ...</button>
</mat-card-actions>
</mat-card>
`,
书籍详细信息。ts-Javascript mods
通过构造函数注入器将服务引入,以调用initTimer()。
使用
@查看子对象
将按钮发送到服务。
export class BookDetailComponent implements AfterViewInit {
// @Inputs and @Outputs as previously defined
constructor(public timerService: PomoTimerService) {}
@ViewChild('resume', {read: ElementRef}) resumeButton;
@ViewChild('pause', {read: ElementRef}) pauseButton;
@ViewChild('reset', {read: ElementRef}) resetButton;
ngAfterViewInit() {
const buttons = {
resumeButton: this.resumeButton,
pauseButton: this.pauseButton,
resetButton: this.resetButton
};
this.timerService.initTimer(buttons);
}