Angular2:可观察对象内部的变化检测

Angular2: Change detection inside observable

本文关键字:内部 变化检测 对象 观察 Angular2      更新时间:2023-09-26

刚刚我尝试了我的第一个应用程序与Angular2 RC3(使用Angular-CLI),我失去了这个…

我对变量word的"变化检测"有一个问题。我在Observable的subscribe方法中更新了word变量,但是没有检测到任何变化。

app.component.ts

import { Component, Inject, OnInit } from '@angular/core';
import { VoiceRecognitionService } from './voice-recognition.service';
@Component({
  moduleId: module.id,
  selector: 'app-root',
  template: `<h1>{{word}}</h1>`, // => No prints any update
  providers: [VoiceRecognitionService],
  styleUrls: ['app.component.css']
})
export class AppComponent implements OnInit {
  private voice: VoiceRecognitionService;
  public word: string = '';
  constructor( @Inject(VoiceRecognitionService) voiceRecognition: VoiceRecognitionService) {
    this.voice = voiceRecognition;
  }
  ngOnInit() {
    this.voice.record('ca')
      .subscribe(word => {
        console.log(word); // => Is printing well the new word
        this.word = `${word}...`; // => No changes detected
      });
  }
}

我记得在Angular 1中使用$scope.$apply来处理类似的情况,我对Angular2进行了相同的搜索,我发现了NgZone.run,我试图在NgZone.run内执行,但没有。

我做错了什么?


额外

:

我与Observable共享服务:

voice-recognition.service.ts

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
export interface IWindow extends Window {
  webkitSpeechRecognition: any;
}
@Injectable()
export class VoiceRecognitionService {
  constructor() {
    /* void */
  }
  /**
   * Record
   * @param {string} language - Language of the voice recognition
   * @returns {Observable<string>} - Observable of voice converted to string 
   */
  record(language: string): Observable<string> {
    return Observable.create((observer) => {
      const { webkitSpeechRecognition }: IWindow = <IWindow>window;
      const recognition = new webkitSpeechRecognition();
      recognition.onresult = (e) => observer.next(e.results.item(e.results.length - 1).item(0).transcript);
      recognition.onerror = observer.error;
      recognition.onend = observer.completed;
      recognition.continuous = true;
      recognition.interimResults = true;
      recognition.lang = language;
      recognition.start();
    });
  }
}

我假设webkitSpeechRecognition API没有被angular zone修补。

要解决这个问题,可以使用zone.run(...)显式地强制执行回到angular区域:

@Injectable()
export class VoiceRecognitionService {
  constructor(private zone:NgZone) {
    /* void */
  }
  /**
   * Record
   * @param {string} language - Language of the voice recognition
   * @returns {Observable<string>} - Observable of voice converted to string 
   */
  record(language: string): Observable<string> {
    return Observable.create((observer) => {
      const { webkitSpeechRecognition }: IWindow = <IWindow>window;
      const recognition = new webkitSpeechRecognition();
      recognition.onresult = (e) => this.zone.run(() => observer.next(e.results.item(e.results.length - 1).item(0).transcript));
      recognition.onerror = (e) => this.zone.run(() => observer.error(e));
      recognition.onend = (e) => this.zone.run(() => observer.completed(e));
      recognition.continuous = true;
      recognition.interimResults = true;
      recognition.lang = language;
      recognition.start();
    });
  }
}

如果回调需要不同数量的参数((e)),请相应地调整代码。

这种方法的优点是该服务的用户不必采取额外的措施。

您最终可以显式调用ChangeDetectorRef类的detectChanges方法:

constructor( @Inject(VoiceRecognitionService) voiceRecognition: VoiceRecognitionService,
    private cdr:ChangeDetectorRef) {
  this.voice = voiceRecognition;
}
ngOnInit() {
  this.voice.record('ca')
    .subscribe(word => {
      console.log(word); // => Is printing well the new word
      this.word = `${word}...`; // => No changes detected
      this.cdr.detectChanges(); // <---------
    });
}