ember中可重用对象的体系结构



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

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

谢谢!

Ember.Component是您的朋友

正如@raulbrito已经提到的,如果你想在ember中使用可重复使用的组件,最好的方法就是使用新的Ember.Component,它在很大程度上基于web组件的新w3草案,从而经得起未来的考验。

我试着举一个简单的例子来说明如何实现这一点。给定一个简单的route,其中model钩子返回一些静态数据:

索引路由

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

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

索引模板

在模板中,我们插入带有{{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将查找一个带有组件camalized名称、后跟Component的类。

因此,由于Ember.Component是从Ember.View扩展而来的,我们可以定义Ember.View支持的各种属性,如tagName。在我们的例子中,我们使用canvas,因为这是chart.js工作所需要的。正如你所看到的,我们还定义了一些attributeBindings来从成员内部控制canvaswidthheight。该组件还定义了一个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钩子解析的延迟,以模拟来自后端的响应,因为您可以看到模板渲染正在等待model承诺首先解析。基本上,我所做的是使用延迟2000ms的Ember.run.later,一旦超时,就可以解决问题:

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,在promise分辨率等待数据时显示一个微调器,LoadingRoute是ember的一个文档较少的功能,您可以在这里阅读更多信息:https://gist.github.com/machty/5647589如何在带有Promises的转换过程中设置(全局)Loading Spinner

请参阅此处获取更新示例:http://jsbin.com/odosoy/145/edit

更新以回应@SamSelikoff的评论

对于上面提到的LoadingRoute@SamSelikoff指出,现在已经有了官方文件:http://emberjs.com/guides/routing/defining-your-routes/#toc_initial-路由

我对此有一些想法,所以只要把它扔出去,以防对你有帮助。

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

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

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

相关内容

最新更新