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