使用ngFor更改数组后DOM未更新

时间:2018-01-08 20:25:44

标签: angular rxjs ngfor

通缉行为:当按下停止按钮时,会生成一个audiolink,添加到一个数组中,其中ngFor会添加到列表中并更新DOM。

实际行为:单击停止按钮后,音频切片不会直接添加到列表中。只有在调用新事件(例如再次单击停止按钮)时,才会通过ngFor将其添加到DOM中(并且工作正常)。

为什么ng不检测audioUrls数组的变化?

APP-component.html:

<button (click)="startRecording()">Start Recording</button>
<button (click)="stopRecording()">Stop Recording</button>
<ul>
  <li *ngFor="let audioUrl of audioUrls">
    <audio controls [src]="audioUrl"></audio> 
  </li>
</ul>

APP-component.ts:

import { RecordService } from './record.service';
import { Component, HostListener } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { SafeUrl, SafeResourceUrl } from '@angular/platform-browser';
import { OnInit } from '@angular/core/src/metadata/lifecycle_hooks';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
  title = 'app';

  audioUrls: SafeResourceUrl[];

  constructor(private recordService:RecordService){
  }

  ngOnInit(){
    this.recordService.audioUrlsChanged.subscribe(list => this.audioUrls = list);
  }

  startRecording(){
    if(this.recordService.audioRecorder){
      this.recordService.audioRecorder.start()
      console.log(this.recordService.audioRecorder.state);
    }
  }

  stopRecording(){
    if(this.recordService.audioRecorder){
      this.recordService.audioRecorder.stop()
      console.log(this.recordService.audioRecorder.state);
    }
  }
}

服务:

import { WindowRefService } from './window-ref.service';
import { Injectable, HostListener } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { SafeUrl } from '@angular/platform-browser';
declare var MediaRecorder: any;

@Injectable()
export class RecordService {

  audioRecorder:any;
  chunks:any[] = [];
  audioUrls:SafeResourceUrl[] = [];
  audioUrlsChanged= new Subject<SafeResourceUrl[]>();

  constructor(private windowRef:WindowRefService, private domSanitizer: DomSanitizer) { 
    this.audioRecorder = this.windowRef.nativeWindow.navigator.mediaDevices.getUserMedia (
      // constraints - only audio needed for this app
      {
         audio: true
      }).then(
        stream => {
          this.audioRecorder = new MediaRecorder(stream);

          this.audioRecorder.ondataavailable = (e) => {
            this.chunks.push(e.data);
          }

          this.audioRecorder.onstop = (e) => {
            let blob = new Blob(this.chunks, { 'type' : 'audio/ogg; codecs=opus' });
            this.chunks = []; //reset buffer
            let url:SafeResourceUrl = this.domSanitizer.bypassSecurityTrustResourceUrl(this.windowRef.nativeWindow.URL.createObjectURL(blob));
            this.audioUrls.push(url);
            this.audioUrlsChanged.next(this.audioUrls.slice());        
          }
      });     
  }

(Window refservice只是窗口对象的包装器)

1 个答案:

答案 0 :(得分:3)

audioRecorder onstop方法发生在Angular上下文之外,因为它是由Angular外部的外部库生成的事件。

因此,Angular不会更新视图。你必须在ngZone.run中运行它。

  1. 将变量zone添加到服务的构造函数中,键入NgZone
  2. 在停止事件中,将代码包装在this.zone.run()
  3. 或者,您可以调用ChangeDetectorRef.detectChanges()。

    请参阅例如this blog post以获得更好的解释: