Ember.js is the glowing spark of an old flame: SproutCore.
I explored SproutCore a few years back and found it impressive, but too far from my scope at the time. It came up again recently at a lunch with some of my co-workers when we were talking about open-source projects that hit bumpy road, and that was the last I heard of it, until recently when I tried Backbone.js. It didn't do quite what I hoped, so I did some research, found Ember and, it seemed, did. Lo and behold: its the burning chars of SproutCore!
There are several remarkable parts of Ember - the router, attribute bindings, object observers, and the view hierarchy, but the data persistence layer needs attention as of late 2012 (note, it is under active development).
Here are some generic code examples that resemble a personal project I'm working on, using Rails 3 (with the pipeline), Bootstrap, and the ember-rails gem (though that needs attention too - see footnote about this).
Without further delay, some Ember.js code examples!
App.Company = DS.Model.extend({ name: DS.attr('string'), country: DS.attr('string'), employeeIds: DS.hasMany('App.Employee') });
Model Notes: I was unable to connect the above hasMany relationship to the same on the Rails side if they were nested, but it was easy to workaround.
App.CompanyView = Em.View.extend({ templateName: 'company', doubleClick: function() { this.showEdit(); } });
View Notes: Remember, Ember views are kinda like Rails controllers.
<div class="well" style="max-width: 340px; padding: 8px 0;"> {{view Bootstrap.NavList contentBinding="controller.content" selectionBinding="App.CompanyController.content"}} </div>
Template Notes: Views can be instantiated from the router (through a connectOutlets controller instantiation), but they can also be instantiated from a template. Handlebars are growing on me (soon they will be literally, thanks to Movember).
App.Router = Ember.Router.extend({ location: 'history', root: Ember.Route.extend({ index: Ember.Route.extend({ route: '/', redirectsTo: 'companies' }), }); });
Router Notes: The Ember.js router is a powerful tool, but its complicated. I hope that some of the complicity it contains can be delegated to controllers at some point. The sticking point may be that the Ember is more that just a Rails router, it contains the state machine that guides the session through the Ember application.
This is one aspect of front-end web development I've had a hard time adjusting to: statefulness. But I like it!
This view guide has the VERY IMPORTANT couple of paragraphs when discussing javascript objects, the DOM, and event delegation:
You are now faced with an uncomfortable choice: create a new view for each item and lose the benefits of event delegation, or create a single view for all of the items and have to store information about the underlying JavaScript object in the DOM.In order to solve this problem, Ember delegates all events to the application's root element (usually the document body) using jQuery. When an event occurs, Ember identifies the nearest view that handles the event and invokes its event handler. This means that you can create views to hold a JavaScript context, but still get the benefit of event delegation.
Further, because Ember registers only one event for the entire Ember application, creating new views never requires setting up event listeners, making re-renders efficient and less error-prone. When a view has child views, this also means that there is no need to manually undelegate views that the re-render process replaces.
These paragraphs help me conceptualize Ember views; here's how I picture them: