我试图调用外部API并将结果用作我的Ember Data模型中的计算属性。结果很好,但计算属性在Promise结算之前返回,导致未定义。这是观察者的用例吗?
export default DS.Model.extend({
lat: DS.attr(),
lng: DS.attr(),
address: Ember.computed('lat', 'lng', function() {
var url = `http://foo.com/json?param=${this.get('lat')},${this.get('lng')}`;
var addr;
var request = new Ember.RSVP.Promise(function(resolve, reject) {
Ember.$.ajax(url, {
success: function(response) {
resolve(response);
},
error: function(reason) {
reject(reason);
}
});
});
request.then(function(response) {
addr = response.results[0].formatted_address;
}, function(error) {
console.log(error);
})
return addr;
})
});
答案 0 :(得分:4)
使用DS.PromiseObject。我一直使用以下技术:
import DS from 'ember-data';
export default DS.Model.extend({
...
address: Ember.computed('lat', 'lng', function() {
var request = new Ember.RSVP.Promise(function(resolve, reject) {
...
});
return DS.PromiseObject.create({ promise: request });
}),
});
将模板中已解析的值用作{{address.content}}
,这将在代理的Promise结算时自动更新。
如果您想在此处执行更多操作,我建议您查看社区中其他人正在做的事情:https://emberobserver.com/?query=promise
构建一个接受DS.PromiseObject
并在Promise仍处于未决状态时显示加载微调器的简单Component并不难,然后一旦显示实际值(或产生一个块)承诺解决。
我在应用程序中有一个Ember.Service
,它几乎完全由Computed Properties组成,返回包含在DS.PromiseObjects中的Promises。它的工作效果令人惊讶。
答案 1 :(得分:1)
我在大型Ember应用程序中使用了self.set('computed_property', value);
技术大约三个月,我可以告诉你它有一个非常大的问题:计算属性只能一次。
当您set
计算的属性值时,生成结果的函数会丢失,因此当您的相关模型属性更改时,计算属性将不会刷新。
在Ember的计算属性中使用promises是一件麻烦事,我找到的最好的技术是:
prop: Ember.computed('related', {
// `get` receives `key` as a parameter but I never use it.
get() {
var self = this;
// We don't want to return old values.
this.set('prop', undefined);
promise.then(function (value) {
// This will raise the `set` method.
self.set('prop', value);
});
// We're returning `prop_data`, not just `prop`.
return this.get('prop_data');
},
set(key, value) {
this.set('prop_data', value);
return value;
}
}),
优点:
{{object.prop}}
,它将正确解析。缺点:
object.get('prop');
并且承诺正在解析时,它会立即返回undefined
,但是如果您正在观察计算属性,则当promise解析时,观察者将再次触发最终值已设定。也许你想知道为什么我没有在get
中回复承诺;如果你这样做并在模板中使用它,它将呈现一个对象字符串表示([object Object]
或类似的东西)。
我想在一个适当的计算属性实现中工作,该实现在模板中运行良好,在Javascript中返回一个promise并自动更新,可能使用DS.PromiseObject或Ember.PromiseProxyMixin,但不幸的是我没有'找时间。
如果大骗局对您的用例不是问题,请使用“获取/设置”技术,如果不尝试实施更好的方法,但严重不要只使用 {{1}它会长期给你带来很多问题,不值得。
PS。:对于这个问题,真正的最终解决方案是:如果可以避免,则永远不要在计算属性中使用promises。
PS。:顺便说一句,这种技术不是我的,而是我的前同事@ reset-reboot。
答案 2 :(得分:0)
创建一个组件(address-display.js):
import Ember from 'ember';
export default Ember.Component.extend({
init() {
var url = `http://foo.com/json?param=${this.get('lat')},${this.get('lng')}`;
Ember.$.ajax(url, {
success: function(response) {
this.set('value', response.results[0].formatted_address);
},
error: function(reason) {
console.log(reason);
}
});
}
});
模板(components / address-display.hbs):
{{value}}
然后使用模板中的组件:
{{address-display lat=model.lat lng=model.lng}}
答案 3 :(得分:-1)
以下工作原理是解析属性并设置结果。
在这里解释: http://discuss.emberjs.com/t/promises-and-computed-properties/3333/10
export default DS.Model.extend({
lat: DS.attr(),
lng: DS.attr(),
address: Ember.computed('lat', 'lng', function() {
var url = `http://foo.com/json?param=${this.get('lat')},${this.get('lng')}`;
var self = this;
var request = new Ember.RSVP.Promise(function(resolve, reject) {
Ember.$.ajax(url, {
success: function(response) {
resolve(response);
},
error: function(reason) {
reject(reason);
}
});
}).then(function(response) {
self.set('address', response.results[0].formatted_address);
})
})
});