在路由中使用 store.query 时通过 websocket 添加新数据

Adding new data through websocket when using store.query in route

我的应用程序使用基于 ember-phoenix 的 websocket 服务将新记录从 API 推送到商店。我希望这些新记录在添加时呈现在我的模板中。

我有一条路线,其中模型挂钩 returns 过滤查询承诺:

import Ember from 'ember';

const {
  get,
  inject,
} = Ember;

export default Ember.Route.extend({
  socket: inject.service(),

  model(params) {
    return this.store.query('my-model', {filter: {date: params.date}})
  },

  afterModel() {
    get(this, 'socket').joinSchedule();
  },

  resetController() {
    get(this, 'socket').leaveSchedule();
  },

});

当新记录通过 websocket 推送到商店时,由于 store.query 的工作方式,我的模板不会呈现它们。如果我将 store.query 更改为 store.findAll,则会呈现新记录,但我希望我的路由仅加载基于日期查询参数的所有记录的子集。

看来我唯一的选择是在将新记录推送到商店时重新加载路由模型。是否可以从服务中执行此操作?如果没有,是否有我可能想探索的不同方法?

我的套接字服务的相关部分如下:

import Ember from 'ember';
import PhoenixSocket from 'phoenix/services/phoenix-socket';

const {
  get,
  inject,
} = Ember;

export default PhoenixSocket.extend({
  session: inject.service(),
  store:   inject.service(),

  joinSchedule() {
    const channel = this.joinChannel(`v1:my-model`);

    channel.on('sync', (payload) => this._handleSync(payload));
  },

  _handleSync(payload) {
    get(this, 'store').pushPayload(payload);
  },
});

当您在套接字上收到一条消息时,您可以从您的 websocket 服务触发一个事件,然后在您的路由中订阅它,然后调用 refresh() 重新加载您的模型。 还有 https://github.com/ember-data/ember-data-filter - 重新调整实时数组。

选项 1
您可以使用Ember.Evented to subscribe and dispatch event. I have created twiddle进行演示。

socket服务中,

  • socket 应该扩展 Ember.Evented class

    export default PhoenixSocket.extend(Ember.Evented, {

  • 更新商店后,您只需触发 myModelDataLoaded 即可调度所有订阅到 myModelDataLoaded 的函数。

     _handleSync(payload) {
            get(this, 'store').pushPayload(payload);
            this.trigger('myModelDataLoaded'); //this will call the functions subscribed to myModelDataLoaded.        
        }

在路线中,

  • 您可以订阅myModelDataLoaded
afterModel() {
        get(this, 'socket').joinSchedule();
        get(this, 'socket').on('myModelDataLoaded', this, this.refreshRoute); //we are subscribing to myModelDataLoaded
    }
  • 定义refreshRoute函数并调用refresh函数。

       refreshRoute() {
            this.refresh(); //forcing this route to refresh
        }
    
  • 为了避免内存泄漏需要off订阅,你可以在resetControllerdeactivate钩子中完成。
    resetController() {
        get(this, 'socket').leaveSchedule();
        get(this, 'socket').off('myModelDataLoaded', this, this.refreshRoute);
    }
    

选项 2.
您可以使用带有观察者和刷新路由的 peekAll 来观看商店。

在你的控制器中,
1. 定义 postModel computed 属性 这将 return 实时记录数组。
2. 定义 postModelObserver 依赖于 postModel.[] 这将确保无论何时使用新行更新存储,myModelObserver 都会观察到它,并将向路由发送操作 refreshRoute。我们将调用 refresh。如您所知,这将调用 beforeModelmodelafterModel 方法。

如您所知,计算 属性 是惰性的,只有在您访问它时才会计算它。所以如果你不在模板中使用它,那么只需在 init 方法

中添加 this.get('myModel')

控制器文件

import Ember from 'ember';
const { computed } = Ember;
export default Ember.Controller.extend({
    init() {
        this._super(...arguments);
        this.get('postModel');//this is just to trigger myModel computed property
    },
    postModel: computed(function() {
        return this.get('store').peekAll('post');
    }),
    postModelObserver: Ember.observer('postModel.[]', function() {
        this.send('refreshRoute');
    })
});

路由文件 - 定义动作refreshRoute用于刷新,因为refresh仅在路由中可用。

import Ember from 'ember';

const {
    get,
    inject,
} = Ember;

export default Ember.Route.extend({
    socket: inject.service(),
    model(params) {
        return this.store.query('my-model', { filter: { date: params.date } })
    },

    afterModel() {
        get(this, 'socket').joinSchedule();
    },

    resetController() {
        get(this, 'socket').leaveSchedule();
    },
    actions:{
        refreshRoute() {
            this.refresh();
        },
    }
});

这不是更好的方法,但处理现有代码的一种方法是使用回调。

import Ember from 'ember';

const {
 get,
 inject,
} = Ember;

export default Ember.Route.extend({
  socket: inject.service(),

  model(params) {
    return this.store.query('my-model', {filter: {date: params.date}})
  },

  afterModel() {
    let cb = (myModelRecord) => {
      this.get('model').push(myModelRecord);
    };
    get(this, 'socket').joinSchedule(cb);
  },

  resetController() {
    get(this, 'socket').leaveSchedule();
  },

});

在套接字服务中调用回调方法,

 import Ember from 'ember';
 import PhoenixSocket from 'phoenix/services/phoenix-socket';

 const {
   get,
   inject,
 } = Ember;

 export default PhoenixSocket.extend({
   session: inject.service(),
   store:   inject.service(),

   joinSchedule(cb) {
     const channel = this.joinChannel(`v1:my-model`);

     channel.on('sync', (payload) => cb(this._handleSync(payload)));
   },

  _handleSync(payload) {
    return get(this, 'store').pushPayload(payload);
  },
});