Architecture for reusable object in ember

后端 未结 2 822
不思量自难忘°
不思量自难忘° 2021-01-30 07:26

I am building an admin dashboard using ember. I want to create a reusable chart object of which I can have multiple instances throughout the application. The chart object should

2条回答
  •  余生分开走
    2021-01-30 08:07

    Ember.Component is your friend

    As @raulbrito already mentioned, the best way to go if you want reusable components in ember is indeed to use the new Ember.Component which is heavily based on the new w3 draft for web components and thus beeing future proof.

    I've tried to make a simple example on how this could be implemented. Given a simple route where the model hook returns some static data:

    Index Route

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

    data and data2 are simply static objects globally defined for simplicity (as you will see in the demo), but this could be also data coming from a backend or from fixtures etc.

    Index template

    In the template then we insert our chart component with the line {{line-chart data=model.modelOne}} and as you can see, we also set the data attribute to the index model model.modelOne or model.modelTwo:

    
    

    Component Template

    Our component template looks fairly simple because it will render a simple canvas element, but it could be as complex as needed, on how to use Ember.Component please refer also to the docs:

    
    

    Component Subclass

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

    Note the naming is important here, Ember knows which subclass powers a component based on its name. For example, if you have a component called line-chart, you would create a subclass called App.LineChartComponent. If your component was called bar-chart-simple, the class name would be App.BarChartSimpleComponent and so on. Ember will look for a class with the camelized name of the component, followed by Component.

    So, and since Ember.Component extends from Ember.View we can define all sorts of properties Ember.View supports like tagName. In our case we use canvas because this is what chart.js needs to work. As you can see we have also defined some attributeBindings to control the width and height of the canvas from inside ember. The component has also a data attribute (which could be called whatever you find appropriate) defined on which we later set our model data in the template returned from the IndexRoute model hook. And finally in your didInsertElement hook of our component we initialize the chart passing with this.get('data') the data object to new created Chart.js class.

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

    And last but not least, please see here for a working example of the above explained.

    Hope it helps.

    Update in response to your last comment

    I've tried to simulate a delay in the resolution of the model hook to mimic a response from a backend, as you can see the template rendering is waiting for the model promise to resolve first. Basically what I've done is to use Ember.run.later with a delay of 2000ms that resolves the promise once timed out:

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

    And just for fun I've also added a LoadingRoute to show a spinner while the promise resolution is waiting for data, the LoadingRoute is a less documented feature of ember, you can read more about it here: https://gist.github.com/machty/5647589 under How do I put up a (global) Loading Spinner during a transition w/ Promises?

    Plase see here for a updated example: http://jsbin.com/odosoy/145/edit

    Update in response to @SamSelikoff's comment

    As for the above mentioned LoadingRoute @SamSelikoff pointed out that it's officially documented now: http://emberjs.com/guides/routing/defining-your-routes/#toc_initial-routes

提交回复
热议问题