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屋!