【问题标题】:EmberJS, polling, update route's model, re-render componentEmberJS,轮询,更新路由模型,重新渲染组件
【发布时间】:2014-09-11 07:45:42
【问题描述】:

我一直在寻找更新路由模型的机制,并让组件(从与该路由关联的模板中调用)对该事件做出反应,并重新渲染自身。

所以我有这样的索引模板(我传入了 IndexController 的模型,据我了解,这只是 IndexRoute 的代理——顺便说一下,我没有定义 IndexController):

<script type="text/x-handlebars" id="index">
  Below is the bar-chart component
  <br/>
  {{bar-chart model=model}}
</script>

我的组件模板是这样的:

<script type="text/x-handlebars" data-template-name="components/bar-chart">
</script>

我的组件是在一个单独的 JS 文件中实现的,像这样:

App.BarChartComponent = Ember.Component.extend({
  classNames: ['chart'],
  model: null,
  chart: BarChart(),
  didInsertElement: function() {
    Ember.run.once(this, 'update');
  },

  update: function() {
    var data = this.get('model').map(function(sales) {
      return sales.get('amount');
    });

    d3.select(this.$()[0]).selectAll('div.h-bar')
      .data(data)
      .call(this.get('chart'));
  }
}); 

BarChart() 函数只是返回一个函数对象,该函数对象执行 DOM 操作以使用 D3 生成图形。

我的 IndexRoute 是这样定义的:

App.IndexRoute = Ember.Route.extend({
  model: function() {
    return this.store.find('sales');
  }
});

在这个实验中,我使用了fixture:

App.Sales = DS.Model.extend({
  amount: DS.attr('number') 
});

idx = 1;

App.Sales.FIXTURES = [
  {id: idx++, amount: 2}, {id: idx++, amount: 6}, {id: idx++, amount: 12}, 
  {id: idx++, amount: 17}, {id: idx++, amount: 8}
];

我需要实现一种机制来定期轮询 store 并更新 Route 的模型,并让 EmberJS 的魔法再次调用渲染函数(分配给 BarChart 组件中“chart”字段的值)。

这样做的正确方法是什么?我一直在尝试使用setInterval并调用Route的refresh()方法,但目前还没有成功。

感谢您的帮助!, 拉卡


添加(我在此处添加了关于格式的附加评论)。

我在 app.js 中添加了对 setInterval 的调用,如下所示:

setInterval(function () { 
  App.Sales.FIXTURES.shift();
  App.Sales.FIXTURES.push({
    id: idx++,
    amount: Math.round(Math.random() * 20)
  });

  App.IndexRoute.refresh();
}, 1500);

但我收到 JavaScript 错误,告诉我 App.IndexRoute 未定义。我打算在 Route 对象上调用“刷新”方法,因为我希望重新执行模型挂钩。如何从我的 setInterval 函数中获取对 IndexRoute 实例的引用?

顺便说一句,这是触发刷新的正确/最佳方式吗?

(并且,根据下面 Oliver 的建议,我还在控制器中的“更新”函数中添加了观察(“模型”)。所以现在是这样的:

App.BarChartComponent = Ember.Component.extend({
  classNames: ['chart'],
  model: null,
  chart: BarChart(),
  didInsertElement: function() {
    ...
  },

  update: function() {
    ...
  }.observes('model')
}); 

ADDITION 2(回复EmberJS, polling, update route's model, re-render component)

知道了!谢谢。

现在对于更新用例(后端中的元素数量保持不变,id 保持不变,只有“数量”随时间变化)。我将 setInterval 块修改为:

setInterval(function () { 
  App.Sales.FIXTURES.forEach(function(elem) {
    elem.amount = elem.amount + 5;
  });

  console.log('z');
}, 1500);

现在的问题是,BarChartComponent 中观察“model.@each”的“update”方法永远不会被调用(好像我在夹具元素中所做的更改没有被 BarChartComponent 听到)。

我需要添加哪些指令?


附加 3(EmberJS, polling, update route's model, re-render component 的详细信息):

我在我的代码中添加了 IndexController 的定义,只是为了确认我对 FIXTURE 中元素的更改至少被控制器听到了(它是)。

所以,现在的问题是组件也能听到这种变化。如何?我应该从我的控制器调用一些“渲染”函数来要求组件重绘自己吗?

App.IndexController = Ember.ArrayController.extend({
  totalAmount: function() {
    console.log("iiii");
    var total = 0;
    this.forEach(function(sales) {
      console.log("a... " + sales.get('amount'));
      total += sales.get('amount');
    });
    return total;
  }.property('@each.amount')
});

【问题讨论】:

  • 在你的组件上不应该是.observes('model')吗?
  • 您好,您的意思是在分配给“更新”字段的函数末尾添加“.observes('model')”?
  • 所以您在索引控制器中的计算属性会更新,但您的组件中的某些属性不会?
  • 嗨,解决了。在条形图组件中,我将观察修改为:model.@each.amount

标签: ember.js model controller routes refresh


【解决方案1】:

App.IndexRoute 实际上是一个类定义,而不是一个实例。

对于您的特定情况,这里有一些重要的事情需要注意,find('type') 返回all 过滤器,该过滤器会在您从商店添加/删除商品时自动更新。因此,您可以在代码中的任何位置(针对该类型)再次调用find,它会自动更新您的集合。此外,您可能希望在路由级别控制更新,这样您就不会在不在范围内时继续更新。

App.IndexRoute = Ember.Route.extend({
  model: function() {
    return this.store.find('sales');
  },
  setupController: function(controller, model){
    this._super(controller, model); // do the default implementation since I'm overriding this func
    this.startRefreshing();
  },
  startRefreshing: function(){
    this.set('refreshing', true);
    Em.run.later(this, this.refresh, 30000);
  },
  refresh: function(){
    if(!this.get('refreshing'))
      return;
    this.store.find('sales')
    Em.run.later(this, this.refresh, 30000);
  },
  actions:{
    willTransition: function(){
      this.set('refreshing', false);
    }
  }
});

示例:http://emberjs.jsbin.com/OxIDiVU/825/edit

此外,对于您的组件,您需要观察数组,而不仅仅是模型本身(因为模型引用不会更新,这意味着不会调用 observes 方法)。你会做这样的事情

watchingForModelChange: function(){

}.observes('model.[]')  

【讨论】:

  • 嗨,它有效!但是有一个小细节。我将上面粘贴的代码复制到了我的 IndexRoute 定义中,并修改了我的 setInterval 块(删除了调用“刷新”的行)。但我注意到一件事:数组中的元素数量一直在增长(我预计它会保持在 5,因为我在推送之前做了一个转变)。可能是因为商店中的缓存吗?你看,我推送夹具的新对象有一个新的 id(时间戳)。我从 FIXTURES 中删除的那个实际上留在了缓存中(?)。如何保持 find 返回的数组长度为 5?
  • 我还是没能理解:在“刷新”函数中调用“this.store.find('sales')”如何导致IndexRoute的模型被刷新?让我猜...检查后端我们必须再次调用“find”方法吗?提前感谢您帮助我理解这一点!
  • 存储缓存它获取的所有记录,因此从 FIXTURES 数组中删除项目不会将其从存储中删除。
  • 如顶部所述,当您最初调用 this.store.find('sales') 时,它会返回 all 过滤器,该过滤器始终包含存储中的所有记录。当我们再次调用this.store.find('sales') 时,您实际上是在回调服务器并获取填充到存储中的结果。您以前的收藏(all 过滤器)将使用商店中的新记录进行更新。
  • 如果您想显示最新的五个或类似的东西,您可能希望在控制器上使用计算属性,只返回最新的五个或类似的东西。
【解决方案2】:

您只需要观看模型。没有?

update: function() {
  var data = this.get('model').map(function(sales) {
    return sales.get('amount');
  });

  d3.select(this.$()[0]).selectAll('div.h-bar')
    .data(data)
    .call(this.get('chart'));
}.property('model')

【讨论】:

    猜你喜欢
    • 2020-07-18
    • 1970-01-01
    • 2019-06-04
    • 2021-08-13
    • 2020-01-02
    • 2018-12-04
    • 2022-11-12
    • 2019-03-02
    • 2020-07-24
    相关资源
    最近更新 更多