Архитектура для объекта многократного использования в ember

Я создаю панель управления admin с помощью ember. Я хочу создать объект многократного использования, из которого я могу иметь несколько экземпляров в приложении. Объект диаграммы должен иметь шаблон, состоящий из некоторой разметки и элемента canvas, для которого мне нужен идентификатор после вставки в DOM, чтобы привязать фактический график (chart.js). Я пробовал несколько подходов, но я не могу понять, как правильно это сделать.

Какова была бы правильная архитектура в ember для достижения вышеуказанного?

Спасибо!

Ответ 1

Ember.Component - ваш друг

Как уже упоминалось в @raulbrito, лучший способ пойти, если вы хотите использовать повторно используемые компоненты в ember, действительно должен использовать новый Ember.Component, который в значительной степени основан на новом проекте w3 для веб-компонентов и, таким образом, будущих доказательств.

Я попытался сделать простой пример того, как это можно реализовать. Учитывая простой route, где hook model возвращает некоторые статические данные:

Маршрут указателя

App.IndexRoute = Ember.Route.extend({
  model: function(){
    return Ember.Object.create({
      modelOne: data,
      modelTwo: data2
    });
  }
});

data и data2 являются просто статическими объектами, глобально определенными для простоты (как вы увидите в демонстрации), но это могут быть также данные, поступающие из бэкэнд или из светильников и т.д.

Шаблон индекса

В шаблоне мы вставляем наш компонент диаграммы в строку {{line-chart data=model.modelOne}}, и, как вы можете видеть, мы также устанавливаем атрибут data в индексную модель model.modelOne или model.modelTwo:

<script type="text/x-handlebars" id="index">
  <h2>Chart one</h2>
  {{line-chart data=model.modelOne}}

  <h2>Chart two</h2>
  {{line-chart data=model.modelTwo}}
</script>

Шаблон компонента

Наш шаблон компонента выглядит довольно просто, потому что он отобразит простой элемент canvas, но он может быть как можно более сложным, как использовать Ember.Component, пожалуйста, обратитесь к документы:

<script type="text/x-handlebars" id="components/line-chart">
</script>

Подкомпонентный подкласс

App.LineChartComponent = Ember.Component.extend({
  tagName: 'canvas',
  attributeBindings: ['width', 'height'],
  width: '480',
  height: '360',
  data: null,
  didInsertElement: function() {
    var ctx = this.get('element').getContext("2d");
    var myNewChart = new Chart(ctx).Line(this.get('data'));
  }
});

Обратите внимание на то, что имя важно здесь, Ember знает, какой подкласс обеспечивает компонент на основе его имени. Например, если у вас есть компонент с именем line-chart, вы должны создать подкласс под названием App.LineChartComponent. Если ваш компонент был вызван bar-chart-simple, имя класса было бы App.BarChartSimpleComponent и так далее. Ember будет искать класс с верблюжным именем компонента, за которым следует Component.

Итак, и поскольку Ember.Component продолжается от Ember.View, мы можем определить всевозможные свойства Ember.View, поддерживающие как tagName. В нашем случае мы используем canvas, потому что это то, что нужно chart.js. Как вы можете видеть, мы также определили некоторый attributeBindings для управления width и height canvas изнутри ember. Компонент также имеет атрибут data (который может быть вызван тем, что вы считаете нужным), на котором мы позже установили наши данные модели в шаблоне, возвращаемом с помощью IndexRoute model. И, наконец, в тэге didInsertElement нашего компонента мы инициализируем передачу диаграммы с this.get('data') объектом данных в новый созданный класс chart.js.

var ctx = this.get('element').getContext("2d");
var myNewChart = new Chart(ctx).Line(this.get('data'));

И последнее, но не менее важное: см. здесь рабочий пример описанного выше.

Надеюсь, что это поможет.

Обновление в ответ на ваш последний комментарий

Я попытался смоделировать задержку в разрешении model hook, чтобы имитировать ответ из бэкэнд, так как вы можете видеть, что рендеринг шаблона ожидает, когда обещание model будет разрешено в первую очередь. В основном то, что я сделал, это использовать Ember.run.later с задержкой в ​​2000 мс, которая устраняет обещание, когда-то истекало:

App.IndexRoute = Ember.Route.extend({
  model: function(){
    return new Ember.RSVP.Promise(function(resolve) {
      Ember.run.later(function() {
        var m = Ember.Object.create({
          modelOne: data,
          modelTwo: data2
        });
        resolve(m);
      }, 2000);
    });
  }
});

И только для удовольствия я также добавил LoadingRoute, чтобы показать прядильщик, в то время как разрешение обещания ожидает данных, LoadingRoute является менее документированной особенностью ember, вы можете прочитать об этом подробнее: https://gist.github.com/machty/5647589 в разделе Как разместить (глобальную) загрузку Spinner во время перехода w/ Promises?

Plase см. здесь для обновленного примера: http://jsbin.com/odosoy/145/edit

Обновление в ответ на комментарий @SamSelikoff

Что касается вышеупомянутого LoadingRoute @SamSelikoff, он отметил, что он официально документирован сейчас: http://emberjs.com/guides/routing/defining-your-routes/#toc_initial-routes

Ответ 2

У меня есть некоторые мысли по этому поводу, поэтому просто бросайте его туда, если это вам поможет.

Прежде всего, я бы посоветовал вам посмотреть презентацию Сэма Селикова на использование Ember с D3. Вся информация здесь: http://www.samselikoff.com/blog/2013/08/09/ember-d3-simple-dashboards/. Кроме того, не пропустите раздел комментариев в сообщении блога.

Это отличный пример использования Ember Views для обертывания объектов D3 и может быть хорошим многоразовым решением. Здесь предостережение заключается в том, что для Ember Views требуется контроллер поддержки, который предоставляет данные. В зависимости от того, где в приложении вы хотите повторно использовать свои диаграммы, это может быть неудобно.

Альтернативой может быть использование компонентов Ember. В этом случае вам просто нужно определить шаблон Component и связанные с ним шаблоны рулей. Хорошая вещь в том, что ему не нужен какой-либо контроллер, поэтому вы освобождаете вас от зависимости, что может облегчить вам добавление такого компонента в разные места вашего приложения. Без конкретного примера мне сложно сделать большой вывод, но, возможно, это поможет вам прояснить ситуацию.