【问题标题】:Angular2 refreshing view on array pushAngular2刷新数组推送视图
【发布时间】:2016-03-27 11:56:09
【问题描述】:

我似乎无法在 array.push 函数上更新 angular2 视图,该函数从 setInterval 异步操作调用。

代码来自这个angular plunkr example of setInterval:

我想要做的如下:

import {View, Component, bootstrap, Directive, ChangeDetectionStrategy, ChangeDetectorRef} from 'angular2/angular2'

@Component({selector: 'cmp', changeDetection: ChangeDetectionStrategy.OnPush})
@View({template: `Number of ticks: {{numberOfTicks}}`})
class Cmp {
  numberOfTicks = [];
  
  constructor(private ref: ChangeDetectorRef) {
    setInterval(() => {
      this.numberOfTicks.push(3);
      this.ref.markForCheck();
    }, 1000);
  }
}

@Component({
  selector: 'app',
  changeDetection: ChangeDetectionStrategy.OnPush
})
@View({
  template: `
    <cmp><cmp>
  `,
  directives: [Cmp]
})
class App {
}

bootstrap(App);
<!DOCTYPE html>
<html>

<head>
  <title>angular2 playground</title>
  <script src="https://code.angularjs.org/tools/traceur-runtime.js"></script>
  <script src="https://code.angularjs.org/tools/system.js"></script>
  <script src="https://code.angularjs.org/tools/typescript.js"></script>
  <script data-require="jasmine" data-semver="2.2.1" src="http://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.1/jasmine.js"></script>
  <script data-require="jasmine" data-semver="2.2.1" src="http://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.1/jasmine-html.js"></script>
  <script data-require="jasmine@*" data-semver="2.2.1" src="http://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.1/boot.js"></script>
  
  <script src="config.js"></script>
  <script src="https://code.angularjs.org/2.0.0-alpha.37/angular2.min.js"></script>
  <script>
    System.import('app')
      .catch(console.error.bind(console));
  </script>

</head>

<body>
  <app></app>
</body>

</html>

如果“numberOfTicks”只是一个数字,上述代码将正常工作(如 plunker 原始示例所示),但一旦我将其更改为数组并推送数据,它就不会更新。

我似乎无法理解为什么会这样。

以下行为类似于我在使用 setInterval / setTimeout 时尝试使用新数据点更新 angular2 中的图表时遇到的问题。

感谢您的帮助。

【问题讨论】:

  • 我会详细说明 - 我正在尝试通过使用上述 setInterval 方法更新它的数据数组来实时更新图表,这个 plunker 可能是一个很好的例子来尝试实现这一点:plnkr.co/edit/vdgKVJOymMYhiyqZrPma?p=preview

标签: javascript arrays asynchronous angular setinterval


【解决方案1】:

在数组中添加元素后,您需要更新数组的整个引用:

  constructor(private ref: ChangeDetectorRef) {
    setInterval(() => {
      this.numberOfTicks.push(3);
      this.numberOfTicks = this.numberOfTicks.slice();
      this.ref.markForCheck();
    }, 1000);
  }
}

编辑

DoCheck 接口也可能会让您感兴趣,因为它允许您插入自己的更改检测算法。

查看此链接了解更多详情:

这是一个示例:

@Component({
  selector: 'custom-check',
  template: `
    <ul>
      <li *ngFor="#line of logs">{{line}}</li>
    </ul>`
})
class CustomCheckComponent implements DoCheck {
  @Input() list: any[];
  differ: any;
  logs = [];

  constructor(differs: IterableDiffers) {
    this.differ = differs.find([]).create(null);
  }

  ngDoCheck() {
    var changes = this.differ.diff(this.list);
    if (changes) {
      changes.forEachAddedItem(r => this.logs.push('added ' + r.item));
      changes.forEachRemovedItem(r => this.logs.push('removed ' + r.item))
    }
  }
}

【讨论】:

  • 谢谢蒂埃里,虽然这确实解决了我在这里描述的情况,但我会详细说明一下。我试图通过更新其值数组来动态更新图形,事情是,如果我对数组进行切片,图形将被完全重绘。在 angular 1.x 中,我使用 $timeout/$interval 执行此操作,它运行 $apply 并进行 angular 以检测更改。我正在尝试使用 angular 2 实现相同的“实时”效果
  • 我会改写 - 使用您在图表中建议的方法将起作用,只有图表会“闪烁”而不是仅在现有图表上绘制一个新点
  • 好的,我明白了。也许你可以在你的组件中注入一个 ChangeDetectorRef 实例,并在数组中添加一个元素后调用它的 markForCheck 方法。
  • 是的,这正是我想要做的,不幸的是,角度似乎仍然需要更改数组引用。但是,我看到的行为很奇怪。如果我在浏览器上执行任何操作,Angular 将更新图表。诸如调整浏览器大小/打开和关闭调试器之类的事情。只要我这样做,图表就会神奇地更新,其行为与角度 1 中的行为相同。
【解决方案2】:

如果“numberOfTicks”只是一个数字,上面的代码可以正常工作,但是一旦我将它更改为数组并推送数据,它就不会更新。我似乎无法理解为什么会这样。

这是因为数字是 JavaScript 原始类型,而数组是 JavaScript 引用类型。当 Angular 更改检测运行时,它使用=== 来确定模板绑定是否已更改。

如果{{numberOfTicks}} 是原始类型,=== 会比较当前值和以前的值。所以01 的值是不同的。

如果{{numberOfTicks}} 是引用类型,=== 将比较当前值和先前(引用)值。所以如果数组引用没有改变,Angular 就不会检测到改变。在您的情况下,使用 push(),数组引用不会改变。

如果您将以下内容放入模板中

<div *ngFor="#tick of numberOfTicks">{{tick}}</div>

然后 Angular 会更新视图,因为每个数组元素都有一个绑定,而不仅仅是数组本身。因此,如果一个新项目是push()ed,或者现有项目被删除或更改,所有这些更改都会被检测到。

因此,在您的图表 plunker 中,当 fromto 数组的内容发生变化时,以下内容应该会更新:

<span *ngFor="#item of from">{{item}}</span>
<span *ngFor="#item of to">{{item}}</span>

好吧,如果每个项目都是原始类型,它将更新。

【讨论】:

  • 这似乎不再起作用了,我得到了“Unexpected token #”错误。
  • 虽然 angular2 似乎不再真正使用 === 了,因为推送到数组就可以了。猜猜他们会做完整的对象比较,包括遍历数组。
猜你喜欢
  • 2019-03-02
  • 1970-01-01
  • 2017-06-08
  • 2017-01-19
  • 2021-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多