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,有时不会。
我猜有时会话恢复先发生,有时动作有线服务先发生。

首先加载动作有线服务时,我预计会发生的情况是:

  1. Action cable 服务已加载,尚未设置当前用户,连接到 public websocket
  2. 处理从会话数据恢复用户的逻辑触发,设置 currentUser.user(发生这种情况,我可以在我的页面上看到用户名)
  3. consumer 计算 属性 注意到 currentUser.user 变化并连接到私人 consumerUrl(没有发生)

我可以用一种不依赖于计算属性的方式很容易地解决这个问题,但我想知道这里出了什么问题。

Computed properties, by default, observe any changes made to the properties they depend on, and are dynamically updated when they're called.

除非您正在调用或调用计算的 属性,否则它不会执行您的预期代码。

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.