EmberJS, polling, update route's model, re-render component

孤街醉人 提交于 2021-02-05 17:41:57

问题


I've been looking for mechanism to update the model of a Route, and has the Component (called from within the template associated with that route) reacts to that event, and re-render itself.

So I have the index template like this (I pass in the model of the IndexController, which to my understanding is just a proxy to IndexRoute -- I don't have IndexController defined, by the way):

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

And I have my component template like this:

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

My component is implemented in a separate JS file, like this:

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'));
  }
}); 

The BarChart() function is simply returns a function object that performs the DOM manipulation to generate the graph using D3.

My IndexRoute is defined like this:

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

During this experiment, I use 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}
];

I need to implement a mechanism to periodically poll the store and update the model of the Route, and has EmberJS's magic invoke again the render function (the value assigned to "chart" field in the BarChart component).

What's the correct way to do that? I've been trying to use setInterval and calling refresh() method of the Route, but have not been successful so far.

Thanks for your help!, Raka


ADDITION (I put my additional comment here for the formatting).

I added the call to setInterval in my app.js, like this:

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

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

But I'm getting JavaScript error, telling me that App.IndexRoute is undefined. I intend to call the 'refresh' method on the Route object because I'm hoping the model hook to be re-executed. How do I obtain a reference to instance of IndexRoute from my setInterval function?

Is this the correct / best way to trigger the refresh, btw?

(and, following the suggestion from Oliver below, I also added observes('model') to my 'update' function in the controller. So it is like this now:

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

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

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

Got it! Thx.

Now for the updating use case (the number of elements in the backend stays the same, the ids stay the same, only the "amount" changes over time). I modified setInterval block to this:

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

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

The problem now, the "update" method in BarChartComponent that observes "model.@each" never gets called (as if the changes I did in the elements of the fixture wasn't heard by the BarChartComponent).

What instruction(s) do I need to add?


ADDITION 3 (detail for EmberJS, polling, update route's model, re-render component ):

I added the definition of IndexController to my code, just to confirm that my changes to the elements in the FIXTURE was heard at least by the Controller (it is).

So, the problem now is making that change is also heard by the Component. How? Should I call some "render" function from my controller to ask the component to redraw itself?

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')
});

回答1:


App.IndexRoute is actually a class definition, not an instance.

For your particular case there are some important things to note here, find('type') returns the all filter which automatically updates as you add/remove items from the store. So you could just call find again anywhere in the code (for that type), and it would automatically update your collection. Additionally you would want to control the updating at the route level, that way you don't keep updating when you aren't in scope.

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);
    }
  }
});

Example: http://emberjs.jsbin.com/OxIDiVU/825/edit

Additionally, for your component, you'd want to watch the array, not just the model itself (since the model reference won't update, meaning the observes method wouldn't be called). You would do something like this

watchingForModelChange: function(){

}.observes('model.[]')  



回答2:


You just need to watch the model. No?

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')


来源:https://stackoverflow.com/questions/24854340/emberjs-polling-update-routes-model-re-render-component

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!