13

我正在使用 ember 构建管理仪表板。我想创建一个可重用的图表对象,我可以在整个应用程序中拥有多个实例。图表对象应该有一个模板,该模板由一些标记和一个画布元素组成,在插入 DOM 后我需要其 id 以附加实际图表 (chart.js)。我尝试了几种方法,但我似乎无法找出正确的架构来做到这一点。

ember 中实现上述目标的正确架构是什么?

谢谢!

4

2 回答 2

34

Ember.Component是你的朋友

正如@raulbrito 已经提到的,如果你想要在 ember 中重用组件,最好的方法是使用新的Ember.Component,它在很大程度上基于web 组件的新 w3 草案,因此是未来的证明。

我试图举一个简单的例子来说明如何实现这一点。给定一个简单routemodel钩子返回一些静态数据的地方:

索引路线

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

data并且data2只是为简单起见全局定义的静态对象(正如您将在演示中看到的那样),但这也可能是来自后端或固定装置等的数据。

索引模板

然后在模板中插入带有线条的图表组件,{{line-chart data=model.modelOne}}如您所见,我们还将data属性设置为索引模型model.modelOnemodel.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.Componentextends fromEmber.View我们可以定义各种属性Ember.View支持,例如tagName. 在我们的例子中,我们使用canvas,因为这是chart.js需要工作的。正如你所看到的,我们还定义了一些attributeBindings来控制内部 ember的width和。该组件还定义了一个属性(可以称为任何您认为合适的名称),我们稍后在从钩子返回的模板中设置我们的模型数据。最后,在我们组件的钩子中,我们初始化图表,将数据对象传递给新创建的类。heightcanvasdataIndexRoute modeldidInsertElementthis.get('data')Chart.js

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

最后但并非最不重要的一点是,请参阅此处以获取上述说明的工作示例。

希望能帮助到你。

更新以响应您的最后评论

我试图模拟model钩子解析的延迟以模拟来自后端的响应,因为您可以看到模板渲染正在等待model首先解决的承诺。基本上我所做的是使用Ember.run.later2000ms 的延迟来解决一旦超时的承诺:

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/5647589How do I put up a (global) Loading Spinner during a transition w/ Promises?

请在此处查看更新的示例:http: //jsbin.com/odosoy/145/edit

更新以回应@SamSelikoff 的评论

至于上面提到的LoadingRoute@SamSelikoff 指出,它现在正式记录在案:http ://emberjs.com/guides/routing/defining-your-routes/#toc_initial-routes

于 2013-08-28T11:22:21.750 回答
3

我对此有一些想法,所以把它扔在那里,以防它帮助你。

首先,我建议您去观看 Sam Selikoff 关于将 Ember 与 D3 结合使用的演示文稿。所有信息都在这里: http: //www.samselikoff.com/blog/2013/08/09/ember-d3-simple-dashboards/。另外,不要错过博客文章的评论部分。

这是使用 Ember 视图包装 D3 对象的一个​​很好的示例,并且可以是一个很好的可重用解决方案。这里需要注意的是,Ember Views 需要一个提供数据的支持控制器。根据您希望在应用程序中重用图表的位置,这可能会带来不便。

另一种方法是使用 Ember 组件。在这种情况下,您只需要定义 Component 和关联的车把模板。它的好处是它不需要任何支持控制器,因此将您从依赖中解放出来,这可能使您更容易在应用程序的不同位置添加这样的组件。如果没有具体的例子,我认为很难得出一个很好的结论,但也许这会帮助你澄清事情。

于 2013-08-28T08:41:25.083 回答