Skip to content

Instantly share code, notes, and snippets.

@bobjackman
Last active December 29, 2015 03:19
Show Gist options
  • Save bobjackman/7607264 to your computer and use it in GitHub Desktop.
Save bobjackman/7607264 to your computer and use it in GitHub Desktop.
Automatic Model Binding between Unregistered Ember Views and Controllers

Normally, if you register views and controllers (with the same name), when they're instantiated, Ember automatically wires them up to each other. view.controller points to the controller you'd expect and both view.content and controller.model are bound to each other. If one changes, so does the other.

However, registering everything with the App means your App namespace gets very polluted, and you have to be very careful choosing your names so you don't collide with views/controllers being defined/used elsewhere in the App. Also, sometimes you have a view/controller that you're only using in one very specific place and it's just kinda silly to have it registered and accessible at a "global" level. In these cases, it's logical to NOT register your views/controllers, but that means things don't get magically wired up by Ember and things don't work the way you'd normally expect.

If you don't register your view/controller, then when you try {{view MyUnregisteredView contentBinding="myModel"}}, you get a view instance, but view.controller is undefined. If you don't need a controller, that's fine, but if you do, then this is a big problem. So you go back to your view definition and tweak it a little:

MyUnregisteredView = Ember.View.extend({
    controller: MyUnregisteredController.create():
});

Now you have a controller and you get back to work. Pretty soon, though, you realize that, in your controller, if you try something like this.get( 'model' ), it doesn't have one - view.content has your model, but view.controller.model is undefined because you didn't bind it. So... back to the view:

MyUnregisteredView = Ember.View.extend({
    controller: MyUnregisteredController.create({
        model: this.get( 'model' ); // -- this refers to view here
    }):
});

Now things work like you'd expect... almost. Done this way, the model is not BOUND to the controller, so if the view's model changes, the controller doesn't get the update. Now your view and your controller are working with different versions or, possibly, entirely different model instances. Now you find yourself coming up with all sorts of convoluted attempts to guarantee your controller is always pointing to the same model as your view and it gets messy. Fast.

After months of wishing for a better way to do this, finding very nearly nothing at all on the interwebz, and not coming up with anything of my own that I was really happy with, I finally came up with this and it works like a charm.

Enjoy!

var MyUnregisteredController = Ember.controller.extend();
var MyUnRegisteredView = Ember.View.extend({
init: function() {
var parentResult = this._super.apply( this, arguments );
// ======== Create and Bind our Controller
this.set( 'controller', MyUnregisteredController.create());
Ember.bind( this, 'controller.model', 'content' );
return parentResult;
}
});
/* --- OR --- */
var MyUnRegisteredView = Ember.View.extend({
controller: Ember.controller.extend(), // -- could also be `create()`
init: function() {
var parentResult = this._super.apply( this, arguments );
// ======== Create and Bind our Controller
var controller = this.get( 'controller' );
if( Ember.typeOf( controller ) != 'instance' ) { // -- we need to instantiate
this.set( 'controller', controller.create());
}
Ember.bind( this, 'controller.model', 'content' );
return parentResult;
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment