Angular2 refreshing view on array push

限于喜欢 提交于 2019-12-05 13:37:03

问题


I can't seem to get angular2 view to be updated on an array.push function, called upon from a setInterval async operation.

the code is from this angular plunkr example of setInterval:

What i'm trying to do is as follows:

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>

The above code will work properly if "numberOfTicks" is just a number, (as the plunker original example shows) but once I change it to an array and push data, it won't update.

I can't seem to understand why is that.

The following behaviour is similar to an issue I have when trying to update a graph in angular2 with new data points when using setInterval / setTimeout.

Thanks for the help.


回答1:


You need to update the whole reference of your array after adding an element in it:

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

Edit

The DoCheck interface could also interest you since it allows you to plug your own change detection algorithm.

See this link for more details:

  • https://angular.io/docs/ts/latest/api/core/DoCheck-interface.html

Here is a sample:

@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))
    }
  }
}



回答2:


The above code will work properly if "numberOfTicks" is just a number, but once I change it to an array and push data, it won't update. I can't seem to understand why is that.

It is because a number is a JavaScript primitive type, and an array is a JavaScript reference type. When Angular change detection runs, it uses === to determine if a template binding has changed.

If {{numberOfTicks}} is a primitive type, === compares the current and previous values. So the values 0 and 1 are different.

If {{numberOfTicks}} is a reference type, === compares the current and previous (reference) values. So if the array reference didn't change, Angular won't detect a change. In your case, using push(), the array reference isn't changing.

If you put the following in your template instead

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

then Angular would update the view because there is a binding to each array element, rather then just the array itself. So if a new item is push()ed, or an existing item is deleted or changed, all of these changes will be detected.

So in your chart plunker, the following should update when the contents of the from and to arrays change:

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

Well, it will update if each item is a primitive type.



来源:https://stackoverflow.com/questions/36247016/angular2-refreshing-view-on-array-push

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!