Skip to content

Instantly share code, notes, and snippets.

@pdokas
Created February 2, 2013 19:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pdokas/4698956 to your computer and use it in GitHub Desktop.
Save pdokas/4698956 to your computer and use it in GitHub Desktop.
How can you delay an Ember.View’s DOM-based code until the Ember Data model has fully loaded?

Ember’s ability to automatically re-render a view when its model changes is awesome. Like “holy shit, that was basically magic” awesome. But there’s a common task that’s so similar but perplexingly more difficult. It’s using data from the model’s records in the view’s jQuery code (or really any clientside DOM library, but Ember bundles jQuery, so let’s be simple and assume jQuery).

For instance, if this is my router:

App.IndexRoute = Ember.Route.extend({
	model: function(params) {
		return App.Gallery.find();
	},
	setupController: function(controller, model) {
		controller.set('galleries', model);
	}
});

And if this is my view:

App.Gallery = DS.Model.extend({
	title: DS.attr('string'),
	photos: DS.hasMany('App.Photo')
});

App.Gallery.FIXTURES = [{
	id: 'canadian_rockies',
	title: 'Canadian Rockies',
	photos: ['photo_1']
}, {
	id: 'cathedrals',
	title: 'Cathedrals',
	photos: ['photo_2']
}, {
	id: 'bryce',
	title: 'Bryce',
	photos: ['photo_3']
}];

Then it follows that I should be able to access the model in my view with this.get('controller.galleries') and indeed, this works well. So far so good!

So at this point in my template I can iterate over and access properties in the galleries array even though both the model and view do their work ASAP without waiting for the other. This all works perfectly well for templates because the view re-renders it automatically when the model changes. Again, holy smokes, magic. But when data from the model is needed in DOM-based view code, things get trickier.

For example, say you want to have a view fade through an array of strings, one by one. And lets say these strings are the names of galleries. To get these strings, it seems like you’d roughly do this:

  1. Register a listener for model change events
  2. When the model says it’s done changing, initialize the StringRotate-a-tron 5000

Registering the listener is fairly easy, the view’s didInsertElement method seems like a reasonable place to do this. Here’s the best way I’ve found to do that:

this.get('controller.galleries').addArrayObserver({
	arrayWillChange: function() { },
	arrayDidChange: function() {
		// initialize the jQuery module
	}
});

But here’s the rub: the observer fires every time the array changes. This is a problem, because you don’t want to initialize your jQuery code repeatedly. And indeed, you don’t want to initialize until the final callback. But there’s no way to tell which callback is the final one. You can’t know if there will be more. Only the model itself knows when it’s done iterating over the data returned from the server/fixtures/flying spaghetti monster. But it seems there’s no callback for this event.

So the question is, how can you delay a view’s DOM-based code until the view’s model has fully loaded?

@trek
Copy link

trek commented Feb 2, 2013

This will depend on the version of ember/ember-data you use, but in later versions records and record arrays are promises and entering a route can be delayed until a promise succeeds.

In other words, a view wouldn't be rendered until an array is ready for it. I think this is broken in pre4 though emberjs/ember.js#1642

@pdokas
Copy link
Author

pdokas commented Feb 2, 2013

@trek Do Ember Data docs (a la http://emberjs.com/api) exist somewhere?

@trek
Copy link

trek commented Feb 2, 2013

They're not published as a website yet, no. But there in there in the source.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment