angular.js - 為什麼 Angular4 異步請求回調中設置值不觸發視圖更新?

查看:134
本文介绍了angular.js - 為什麼 Angular4 異步請求回調中設置值不觸發視圖更新?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问 题

直接上代碼先:

export class HeaderComponent {
  constructor() {}
  
  currentUser = null

  signin () {
    AV.User.logIn(this.model.username, this.model.password).then((loginedUser) => {
      this.currentUser = loginedUser
    })
  }
}

這裡用 Leancloud 實現的是一個登錄的方法,登錄後,this.currentUser = loginedUser 賦值成功,但視圖並沒有更新。

在 AngularJs 1.x 中,我知道是需要主動觸發更新的,那在 Angular4 中又是怎樣觸發更新的呢?用 Observable 或者 Promise 還是其他,有沒有更便捷的方法。


測試過,Promise 是可行的,Observable 應該也可以的吧。(就是搞不懂為什麼異步回調裡賦值跟同步賦值的結果會不一樣,還要在請求外套東西)。還有方法就是用 httpClient 來請求 Leancloud 的 rest API。

Promise.resolve().then(() => {
  return AV.User.logIn(this.model.username, this.model.password)
}).then((loginedUser) => {
  this.currentUser = loginedUser
}).catch(() => {
})

getBusiness () {
  return new Observable<any>((observer) => {
    var query = new AV.Query('Business')
    query.find().then((results) => {
      observer.next(results)
      observer.complete()
    }).catch((error) => {
      observer.error(error.message)
    })
  })
}

constructor() {
  this.getBusiness().subscribe((list) => {
    console.log(list)
    this.business = list
  })
}

解决方案

太长不读版:

因为 Leancloud 用的不是 Promise。

太长也要读版:

Angular 对于异步任务的检测依赖于 Zone.js,而 Zone.js 实现的原理是事先针对 所有可能 的异步 API 进行埋点,从而劫持相应的方法以实现监听。

那么问题来了,Zone.js 如何得知所有可能的异步 API 呢?

答案当然是:没办法的。所以 Zone.js 默认只会劫持对标准的 Web API 或者 JavaScript API 中定义的异步过程,具体的列表位于 STANDARD-APIS.md 文档中。对于不在标准中的,但是有常用的第三方 API,也提供了一定的支持,具体的支持情况位于 NON-STANDARD-APIS.md 文档中。

所以很明确的一点,对于既不是标准 API,又不是 Zone.js 特别支持的常用 API 的话,是无法正确最终异步上下文的。

那么另一个问题是,Leancloud SDK 到底用的是什么呢?我们来看看源码(leancloud/javascript-sdk):

var _ = require('underscore');
var Promise = require('es6-promise').Promise;

Promise._continueWhile = function(predicate, asyncFunction) {
  if (predicate()) {
    return asyncFunction().then(function() {
      return Promise._continueWhile(predicate, asyncFunction);
    });
  }
  return Promise.resolve();
};

module.exports = Promise;

这里可以很清晰的看出,Leancloud SDK 使用的是扩展过的 es6-promise 这个 NPM Package,而且并不是作为 Polyfill 使用的,而是作为 Library 使用的。

如果打开它 打包后的文件,可以看到其中有一个 Promise$2 类型:

function Promise$2(resolver) {
  this[PROMISE_ID] = nextId();
  this._result = this._state = undefined;
  this._subscribers = [];

  if (noop !== resolver) {
    typeof resolver !== 'function' && needsResolver();
    this instanceof Promise$2 ? initializePromise(this, resolver) : needsNew();
  }
}

这就是内联后的样子。所以说,对于 Leancloud SDK 而言,只是一个兼容 Promise API 的普通的第三方库,而并非 Promise。于是乎 Zone.js 对此当然也是无能为力的。

所以简单地说,就是 Leancloud 自己的实现过于 Tricky 且远离现有的 Web 规范,不被支持很正常。

那虽然不是 Angular 的问题,要如何补救呢?

最简单的方案是使用 NgZone#run() 方法进行包装,形如:

AV.User.logIn(this.model.username, this.model.password).then((loginedUser) => {
  this.ngZone.run(() => {
    this.currentUser = loginedUser
  })
})

这样就能重新将异步上下文重置为 Angular 内,以保证得到正确的监听。

这篇关于angular.js - 為什麼 Angular4 異步請求回調中設置值不觸發視圖更新?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆