Angular 2直到点击任意对象后才会更新

6
以下是我试图基于Angular教程使其工作的内容。
export class DashboardComponent implements OnInit {
      title = 'Current Robots';
      private sobots: Robot[];

      constructor(
        private router: Router,
        private sobotService: RobotService) { 
      }

      getRobots() { 
          this.sobotService.getRobots().then(sobots => Array.prototype.push.apply(this.sobots, sobots.data));     
      }

      ngOnInit() {
          this.getRobots();
      }

      gotoDetail(sobot: Robot) {
        let link = ['/detail', sobot.Id];
        this.router.navigate(link);
      }

视图(View)

<h3>{{title}}</h3>
<div class="grid grid-pad">        
  <div *ngFor="let sobot of sobots" (click)="gotoDetail(sobot)" class="col-1-4">
    <div class="module sobot">
      <p>HostKey</p>
      <h4>{{sobot.HostKey}}</h4>
    </div>
  </div>
</div>
<sobot-search></sobot-search>
看起来已经按预期返回了数据对象,但是直到我点击任何按钮/路由/触发任何事件之前,它仍然没有更新。

控制台中没有错误显示,我很困惑!

我已经尝试确保它只向原始数组对象添加数据,但即使这样也似乎不起作用!

5个回答

7

事实证明,sobotService返回的是q.js promise而不是ES6 promise。我相信这可能就是为什么它没有像在Heroes教程中那样按照我的期望进行更新的原因。

通过强制在zone中更新它,例如:

  this.sobotService .getRobots().then(ODataRobots => {
      this.zone.run(() => this.sobots = ODataRobots.data);          
  });

一旦承诺被解决,它成功更新了视图。

我可能需要找出一种简单的方法来从我正在使用的库(o.js)中删除q.js promise,但在此期间,这个代码将按预期工作!


我会扩展这个答案,包括如何利用NgZone。以下文章完美地解释了它。https://blog.thoughtram.io/angular/2016/02/01/zones-in-angular-2.html - David Aguirre

3

解决方案

正如阿卜杜勒·瓦希德提到的那样,修改状态的代码需要使用ngZone服务进行包装:

  // `ngZone` is the instance of the `NgZone` service.
  this.ngZone.run(() => {
    this.myState = 'new value';
  });

但是正如Madhur Bhaiya所指出的那样,它并不够详细,所以在这里我会解释得更加详细一些。

更大的例子

这个例子是由José Pedro Silva提供的,在GitHub Angular issues page上。

public constructor(private ngZone: NgZone) {
}

public async update() {
  try {
    this.viewVariable = null;
    let newValue = await this.dataService.getAsync();

    this.ngZone.run(() => {
      this.viewVariable = newValue;
    });
  }
  catch (error) {
    this.viewVariable = error;
  }
}

解释

当异步代码(在 Angular 范围之外)修改状态时,它不会触发视图更新。单击可以完成此操作。NgZone 服务可以解决此问题。

该技术对于 AJAX 调用以及任何异步调用都很有帮助,比如外部库回调(这是我的情况)。


1
在我的情况下,问题是由于应用程序组件中的changeDetection: ChangeDetectionStrategy.OnPush引起的。 从应用程序组件中删除changeDetection: ChangeDetectionStrategy.OnPush,它就可以正常工作了。

1
尝试为this.sobots分配一个初始值。
export class DashboardComponent implements OnInit {
      title = 'Current Robots';
      private sobots: Robot[] = []; <---- this

      constructor(
        private router: Router,
        private sobotService: RobotService) { 
      }

      getRobots() { 
          this.sobotService.getRobots().then(sobots => Array.prototype.push.apply(this.sobots, sobots.data));     
      }

      ngOnInit() {
          this.getRobots();
      }

      gotoDetail(sobot: Robot) {
        let link = ['/detail', sobot.Id];
        this.router.navigate(link);
      }
}

否则,您正在尝试将数据推送到一个空对象,我认为 push.apply 不会起作用。在 Node.js 中:
> let x
undefined
> x
undefined
> Array.prototype.push.apply(x,['y','z'])
TypeError: Array.prototype.push called on null or undefined
    at repl:1:22
    at REPLServer.defaultEval (repl.js:272:27)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:96:13)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:224:10)
    at REPLServer.Interface._line (readline.js:566:8)
    at REPLServer.Interface._ttyWrite (readline.js:843:14)

好的观点!那是问题的一部分。最终还是没有解决,但这确实帮助我找到了问题所在。干杯 :) - Nathan

1
这帮了我一个大忙。
this.someService.captureDetails(data).subscribe((res) => {
  this.zone.run(() => { 
   this.data = res ;
    }
  });
});

1
请添加更多关于它如何帮助你的细节,并对其中重要的步骤进行解释。 - Madhur Bhaiya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接