EmberJS, опрос, обновление модели маршрута, компонент рендеринга

Я искал механизм для обновления модели Маршрута, и чтобы Компонент (вызываемый из шаблона, связанного с этим маршрутом) реагировал на это событие и повторно отображал сам себя.

Таким образом, у меня есть такой шаблон индекса (я передаю модель 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');
  }
});

Во время этого эксперимента я использую крепеж:

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

Мне нужно реализовать механизм для периодического опроса хранилища и обновления модели Маршрута, и теперь волшебное средство EmberJS снова вызывает функцию рендеринга (значение, назначенное полю "chart" в компоненте BarChart).

Какой правильный способ сделать это? Я пытался использовать setInterval и вызывать метод refresh() для Route, но пока не увенчался успехом.

Спасибо за помощь!, Рака


ДОПОЛНЕНИЕ (я разместил здесь свой дополнительный комментарий для форматирования).

Я добавил вызов setInterval в мой app.js, вот так:

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 не определен. Я намереваюсь вызвать метод refresh для объекта Route, потому что я надеюсь, что перехват модели будет выполнен повторно. Как получить ссылку на экземпляр IndexRoute из моей функции setInterval?

Это правильный / лучший способ вызвать обновление, кстати?

(и, следуя предложению Оливера, приведенному ниже, я также добавил наблюдатели ("модель") в свою функцию "обновления" в контроллере. Так что теперь это так:

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

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

ДОБАВЛЕНИЕ 2 (ответ на EmberJS, опрос, обновление модели маршрута, компонент повторного рендеринга)

Понял! Спасибо.

Теперь для варианта использования обновления (количество элементов в бэкэнде остается тем же, идентификаторы остаются теми же, только "количество" изменяется со временем). Я изменил блок setInterval к ​​этому:

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

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

Проблема теперь в том, что метод "update" в BarChartComponent, который наблюдает "model.@ Each", никогда не вызывается (как если бы изменения, которые я сделал в элементах прибора, не были услышаны BarChartComponent).

Какие инструкции мне нужно добавить?


ДОБАВЛЕНИЕ 3 (подробности для EmberJS, опрос, обновление модели маршрута, компонент повторного рендеринга):

Я добавил определение 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')
});

2 ответа

Решение

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

Кроме того, для вашего компонента вы захотите наблюдать за массивом, а не только за самой моделью (поскольку ссылка на модель не будет обновляться, что означает, что метод наблюдений не будет вызываться). Вы бы сделали что-то вроде этого

watchingForModelChange: function(){

}.observes('model.[]')  

Вам просто нужно посмотреть модель. Нет?

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')
Другие вопросы по тегам