变量更改时 Angular2 组件视图不更新

Angular2 component view not updating when variable changes

我有一个只呈现进度条的简单组件。

它初始化正常,进度也很好,但模板没有更新新值。

import {Component} from 'angular2/core';

@Component({
  selector : 'progress-bar',
  template : `
    <div class="progress-container">
      <div>{{currentProgress}}</div>
      <div [style.width.%]="currentProgress" class="progress"></div>
    </div>
  `,
  styles: [
    '.progress-container {width: 100%; height: 5px}',
    '.progress {background-color: #8BC34A; height:5px}'
  ]
})

export class ProgressBar {
  currentProgress: number;

  constructor() {
    this.currentProgress = 0;
  }

  onProgress(progress: number) {
    console.log(progress) //consoles correct percentages
    this.currentProgress = progress;
  }

  reset() {
    console.log(this.currentProgress) //is 100
    this.currentProgress = 0;
  }
}
~

别处

  ngOnInit() {
    this.httpFileService.progress$.subscribe((progress: number) => this.onProgress(progress));
  }

  onProgress(progress: number) {
    this.progressBar.onProgress(progress*100);
  }

我觉得我缺少了一些非常补救的东西。

你正在以一种违背框架的方式来解决这个问题,并且会导致大量的哀号和咬牙切齿。

现在,您正在手动订阅一个可观察对象 - httpFileService.progress$ - 然后手动更新子 ProgressBar 组件上的 属性,绕过 angular' s 变化检测机制——这就是 UI 没有更新的原因。您 可以 在设置此 属性 后手动触发更改检测,并且 UI 将按预期更新 - 但是同样,您将与框架作对,所以让我们看看如何使用它:

我假设 "elsewhere" 是您的 ProgressBar 组件的父级 - 我们称其为 ElsewhereComponent

@Component({
  selector: 'elsewhere',
  directives: [ProgressBar],
  template: ` 
    <div>
      <progress-bar [currentProgress]="httpFileService.progress$"></progress-bar>
    </div>  
   `
})
class ElsewhereComponent { 
  // you can remove the ngOnInit and onProgress functions you posted
  // you also don't need a reference to the child ProgressBar component
  // ... whatever else you have in this class ...
}

这里要注意的最重要的事情是在 progress-bar 组件上添加 [currentProgress]:这告诉 angular 有一个输入 属性 名为 currentProgress 在应该绑定到 httpFileService.progress$ 的组件上。

但你现在对 angular 撒了谎 - 就目前而言,ProgressBar 根本没有输入,angular 会在它试图绑定这个不存在的东西时告诉你属性 到给定值。所以我们需要添加输入 属性,而这样做的首选方法是使用 Input() 装饰器:

@Component({
  selector : 'progress-bar',
  pipes: [AsyncPipe]  //import this from angular2/core
  template : `
    <div class="progress-container">
      <div>{{currentProgress | async}}</div>
      <div [style.width.%]="currentProgress | async" class="progress"></div>
    </div>
  `
})
export class ProgressBar {
  @Input() currentProgress: Observable<number>;
  ...
  constructor(){ 
     // remove the line you have in here now
  }

}

这里有两个关键区别需要注意:首先,@Input() 告诉 angular currentProgress 是一个输入 属性。我们还将 属性 的类型从 number 更改为 Observable<number> - 这不是绝对必要的,但它很有用,因为它允许第二个关键差异:

AsyncPipe 已添加到组件的 pipes,并在其到 currentProgress 的两个模板绑定中使用。这很有用,因为它告诉 angular 处理所有订阅 Observable 和更新 UI 每次发出新值时的脏活。

这就是它所需要的全部内容:栏的宽度和上面的文本现在都会自动更新以反映从 httpFileService 发出的值,而且您不必写一行的 命令式代码来实现它。