Javascript components and Ember
Often when using Ember, it's tempting to integrate external javascript components. Sometimes this is trivial and easy, but often it can be both difficult and counterproductive. Here I will try to explain the pros, cons and reasoning behind them.
When it makes sense: self-contained components.
When using external components, you have to decide whether the external component has responsibility for managing the DOM or ember does. If the responsibility can be handed off entirely to the external component, it can be a good choice. For example, imagine a calendar popup component that is a jquery plugin, triggered with this imaginary api:
var calendar = $(someElement).calendar({
onChange: function(date) {
alert(date);
}
});
calendar.setDate(new Date());
In this case, the internals of the calendar are opaque and we don't want Ember to manage any of the dom. This can be trivially wrapped in an ember component like so:
App.SomeCalendarComponent = Em.Component.extend({
currentDate: null,
initCalendar: (function() {
var _this = this;
// this.$() returns a jQuery object scoped to the component's DOM element
this.calendar = this.$().calendar({
onChange: function(date) {
_this.set('currentDate', date);
}
});
}).on('didInsertElement'),
updateCalendarDate: (function() {
// the date changed elsewhere, so update the displayed date
this.calendar.setDate(this.get('currentDate'));
}).observes('currentDate')
});
This component can now be easily used in your templates like so, binding the date property to a variable of your choosing:
{{some-calendar currentDate=someVariable}}
EvilTrout has a great tutorial that goes into this in more detail.
But can you do better?
Maybe. There are two cases when the above may not be the best approach.
- Has someone already made an ember specific version of what you want? For example, http://emberui.com/documentation/calendar. This can both save you hassle and maintenance and integrate better out of the box, including better variable binding and runloop integration.
- Do you need lots of customization? Ember provides massively powerful tools for building complex UIs. If you want something that you can customise to your precise specifications, then rolling your own will probably pay off long term.
When it very rarely makes sense: components that wrap a lot of DOM.
For example, a modal component. Treating external components as a "black box" is quite trivial to integrate. However if you want your component to have DOM managed by Ember inside of it you will almost always regret trying to shoehorn external libraries into this role. The reason for this is that there is now an unclear seperation of concerns between which library is responsible for which part of the DOM. You need to manually keep track of the rendering flow of e.g.
Ember renders container → Plugin renders modal container and animates → Ember renders modal contents
The reverse must be handled for teardown, and it can get very complex when trying to square the expectations of the external library with the expectations of the ember runloop and rendering process. Whilst it can be done, and is sometimes unavoidable, in my experience you will almost always write less, and simpler code just using ember's built in powertools to build what you need, rather than trying to bash something into shape to work in this role.
In addition, for the major use-cases there are often pre-built ember-specific components that will be easier to adapt for your use case than trying to make the non-ember library do what you need. This is doubly the case as soon as you have very specific custom requirements for your UX interactions - customising ember specific code will make your life a lot easier, give you more flexibility, and take less time in the long run.
Two examples of common components that are relevant to this discussion are:
In short, I would argue that rolling your own components often corresponds to the red line on this graph, whist non-ember components corresponds to the blue line: