Ember 计算 属性 取决于服务 属性 未更新
Ember computed property depending on service property not updating
在我的 Ember 2.8 应用程序中,我正在服务中建立 Websocket 连接。当用户登录时,连接 URL 发生变化(然后它包含用户身份验证令牌作为查询参数)。
当前用户服务简单:
CurrentUserService = Ember.Service.extend(
name: "current-user"
user: null
load: ->
// Do some stuff
@set("user", user)
)
它完全符合预期,我用它在页面上显示当前用户的用户名(以及其他内容)。
在 Websocket 服务中,我所做的就是创建一个计算的 属性,这取决于 currentUser.user,它会建立连接(取决于用户是否登录):
ActionCableService = Ember.Service.extend(
name: "action-cable"
cable: service()
currentUser: service()
testObs: Ember.observer("currentUser", ->
console.log "currentUser changed, #{ @get("currentUser.user") }"
)
consumer: Ember.computed("currentUser.user", ->
consumerUrl = "ws://localhost:10000/cable"
if @get("currentUser").user?
consumerUrl += "?token=#{ @get("currentUser.user.authToken") }"
console.log(consumerUrl)
return @get("cable").createConsumer(consumerUrl)
)
)
问题是,消费者 属性 永远不会更新。它在页面加载时设置一次,当 currentUser 服务的用户 属性 发生变化时,consumer
不会更新,我的测试观察者也不会。
刷新页面时,有时会使用登录的consumerUrl,有时不会。
我猜有时会话恢复先发生,有时动作有线服务先发生。
首先加载动作有线服务时,我预计会发生的情况是:
- Action cable 服务已加载,尚未设置当前用户,连接到 public websocket
- 处理从会话数据恢复用户的逻辑触发,设置
currentUser.user
(发生这种情况,我可以在我的页面上看到用户名)
consumer
计算 属性 注意到 currentUser.user
变化并连接到私人 consumerUrl(没有发生)
我可以用一种不依赖于计算属性的方式很容易地解决这个问题,但我想知道这里出了什么问题。
除非您正在调用或调用计算的 属性,否则它不会执行您的预期代码。
Observers
,另一方面,当 属性 他们正在观察时发生变化时,无需调用即可做出反应。但是它们经常被过度使用,并且由于它们的同步性质很容易引入错误。
您可以将观察者和计算属性重构为直接调用的辅助函数。这也使它们更容易进行单元测试。
在您的控制器中,您可以处理登录的初始操作,如下所示:
currentUser: Ember.inject.service(),
actions: {
login() {
this.auth({ username: 'Mary' });
},
},
auth(data) {
// Send data to server for authentication...
// ...upon response, handle the following within the promise's `then`
// method, failures caught within `catch`, etc. But for purposes of
// demonstration, just mocking this for now...
const response = { username: 'Mary', authToken: 'xyz', };
this.get('currentUser').setConsumer(response);
},
当前用户服务然后可以设置它的属性,并在action-cable
服务上调用辅助函数:
actionCable: Ember.inject.service(),
authToken: null,
username: null,
setConsumer(response) {
this.set('authToken', response.authToken);
this.set('username', response.username);
this.get('actionCable').setConsumer();
},
action-cable 服务 从 currentService
读取属性,设置 consumerUrl
,并调用 cable 服务创建消费者:
cable: Ember.inject.service(),
currentUser: Ember.inject.service(),
setConsumer() {
var consumerUrl = "ws://localhost:10000/cable";
if (this.get("currentUser.username") !== null) {
consumerUrl += "?token=" + (this.get("currentUser.authToken"));
}
console.log("ACTION CABLE SERVICE, Consumer URL: ", consumerUrl);
this.get("cable").createConsumer(consumerUrl);
}
我创建了一个 Ember Twiddle 来演示。
另一种方法是在服务中发出一个 event 然后在 init 方法中订阅事件并设置计算的 属性 的依赖键的值以强制它成为 recomputed/updated.
在我的 Ember 2.8 应用程序中,我正在服务中建立 Websocket 连接。当用户登录时,连接 URL 发生变化(然后它包含用户身份验证令牌作为查询参数)。
当前用户服务简单:
CurrentUserService = Ember.Service.extend(
name: "current-user"
user: null
load: ->
// Do some stuff
@set("user", user)
)
它完全符合预期,我用它在页面上显示当前用户的用户名(以及其他内容)。
在 Websocket 服务中,我所做的就是创建一个计算的 属性,这取决于 currentUser.user,它会建立连接(取决于用户是否登录):
ActionCableService = Ember.Service.extend(
name: "action-cable"
cable: service()
currentUser: service()
testObs: Ember.observer("currentUser", ->
console.log "currentUser changed, #{ @get("currentUser.user") }"
)
consumer: Ember.computed("currentUser.user", ->
consumerUrl = "ws://localhost:10000/cable"
if @get("currentUser").user?
consumerUrl += "?token=#{ @get("currentUser.user.authToken") }"
console.log(consumerUrl)
return @get("cable").createConsumer(consumerUrl)
)
)
问题是,消费者 属性 永远不会更新。它在页面加载时设置一次,当 currentUser 服务的用户 属性 发生变化时,consumer
不会更新,我的测试观察者也不会。
刷新页面时,有时会使用登录的consumerUrl,有时不会。
我猜有时会话恢复先发生,有时动作有线服务先发生。
首先加载动作有线服务时,我预计会发生的情况是:
- Action cable 服务已加载,尚未设置当前用户,连接到 public websocket
- 处理从会话数据恢复用户的逻辑触发,设置
currentUser.user
(发生这种情况,我可以在我的页面上看到用户名) consumer
计算 属性 注意到currentUser.user
变化并连接到私人 consumerUrl(没有发生)
我可以用一种不依赖于计算属性的方式很容易地解决这个问题,但我想知道这里出了什么问题。
除非您正在调用或调用计算的 属性,否则它不会执行您的预期代码。
Observers
,另一方面,当 属性 他们正在观察时发生变化时,无需调用即可做出反应。但是它们经常被过度使用,并且由于它们的同步性质很容易引入错误。
您可以将观察者和计算属性重构为直接调用的辅助函数。这也使它们更容易进行单元测试。
在您的控制器中,您可以处理登录的初始操作,如下所示:
currentUser: Ember.inject.service(),
actions: {
login() {
this.auth({ username: 'Mary' });
},
},
auth(data) {
// Send data to server for authentication...
// ...upon response, handle the following within the promise's `then`
// method, failures caught within `catch`, etc. But for purposes of
// demonstration, just mocking this for now...
const response = { username: 'Mary', authToken: 'xyz', };
this.get('currentUser').setConsumer(response);
},
当前用户服务然后可以设置它的属性,并在action-cable
服务上调用辅助函数:
actionCable: Ember.inject.service(),
authToken: null,
username: null,
setConsumer(response) {
this.set('authToken', response.authToken);
this.set('username', response.username);
this.get('actionCable').setConsumer();
},
action-cable 服务 从 currentService
读取属性,设置 consumerUrl
,并调用 cable 服务创建消费者:
cable: Ember.inject.service(),
currentUser: Ember.inject.service(),
setConsumer() {
var consumerUrl = "ws://localhost:10000/cable";
if (this.get("currentUser.username") !== null) {
consumerUrl += "?token=" + (this.get("currentUser.authToken"));
}
console.log("ACTION CABLE SERVICE, Consumer URL: ", consumerUrl);
this.get("cable").createConsumer(consumerUrl);
}
我创建了一个 Ember Twiddle 来演示。
另一种方法是在服务中发出一个 event 然后在 init 方法中订阅事件并设置计算的 属性 的依赖键的值以强制它成为 recomputed/updated.