Ember.js Controllers for the Absolute Beginner

Controllers

Mastering controllers in Ember has it’s perks. I’ve found that I tend to slowly reduce all of my views into components as time carries on. This has two interesting side effects.

First, you’ll depend less on events like ‘clicking’ or ‘touching’ and only use them to trigger actions on the controller via Ember.ViewTargetActionSupport. Second, and I think this is the cooler effect of the two, you’ll begin to see clear and descriptive action names rather than what usually gets described as a comment within a click handler of a view.

Controllers in Ember are vast. They contain endless functionality for you to utilize, and are really well documented.

The root Controller API docs are here: http://emberjs.com/api/classes/Ember.Controller.html

Do’s and Do not’s

Smart use of get() and set() help ember do all of their clever observables and data binding. Using ‘raw accessors’ on properties can sometimes result in observables not firing. Always use get() and set() for single property operations, while using getProperties() and setProperties() for anything that sets many things.

Be smart about using things like incrementProperty() and decrementProperty(). Sometimes being more expressive is worth a trivial performance penalty.

I find it’s incredibly useful for keeping mentally organized is routinely prune your controller classes into logical groups. Think #REGION or something similar.

Actionable Stuff

Actions, as an abstraction of events, even if it’s just semantics, seems to help me add a nice layer between a direct view manipulation operation and business logic rules. This layer between the view logic of “Hey, the mouse moved!”, and the controller logic of “The user moved their mouse into this area that shows a contextual menu, I better update what I’m displaying so they see it” into easy to organize, declarative functions that describe exactly what my implementation needs to fulfill.

Think of the abstraction of an interface between these two things. According to SOLID, we should follow the Single Responsibility principle to split things between what the user is doing and what information it requires so that the user can do that thing. Tracking position data of the users mouse versus what regions are interesting for the mouse to be in require completely different schools of implementation details.

I realize this sounds a bit like rudimentary MVC – but that’s the point I’m making. Ember’s MVC model is effectively the french champaign to other javascript MVC’s Coors Light. You should think about how easy it is to work within your codebase as a way of increasing how much you can get done with it versus other options. It’s about luxury. The best part about luxury is that things are easier.

Think long and hard about what parts of what you’re doing are concrete enough to unit test versus an integration test.

This is my basic ‘theory’ of controller design: If they are more easily unit tested, you should put them in a view. If they’re easier to integration test, then put them in your controllers.

Okay. Right. Controllers.

There are three types of controllers you can define:

  • Ember.Controller: “I don’t have a direct model.”

I tend to use base controllers for things that I need to register and inject into other controllers or models. Also useful as an effective grouping of business logic that does not require to be instantiated multiple times. Controllers without a template are fine.

  • Ember.ObjectController: “I have one and only one Ember.Object as a model”

Object controllers tend to populate my root resources as collective state of the application within that resource tree. An example of this is defining something like this.resource(‘posts’) in my router. I have a posts.index route, a posts.list route, and a posts.single route. They do about what you expect. For reasons cooked up in the marketing department, we have to track how many links a user has clicked on that contain the class of “.track” because everyone in marketing has lost their minds. “Fine”, you say, and get to work on implementing this preposterous feature. You’ll probably need to choose the correct abstraction layer for the data we’ll be tracking. Where does that go?

I’ve found that these sorts of items work well as a single Model, which is basically a fancy way of saying “an Ember.Object”. Ember doesn’t define a “Model” class to subclass from. Just Objects. Go with that and pass it as the model (or a property on the Ember.Route sitting at the top of things) and assign it to the controller during Ember.Route.setupController()’s hook.

Define a few actions at the route level that manipulate that model definition, and now you can call the controller’s this.send(‘actionName’, params) to bubble up from your controller to the route actions, and now you can manage state across multiple child route changes from a resource object. Go make some coffee and curse your co-workers in marketing.

  • ArrayController: “Many Instances of like Objects”

The array controller expects that the root datatype returned from the model hook is an Array. There are some sugar bonuses through using this subclass, such as {{#each}} support in your templates.

Being efficient

Using cacheFor(‘prop’) is helpful when using computed properties. This will access the current computed state without recalculating it.

using has(‘prop’) will bubble. This means you can check for a particular state property or some similar case without having to worry about all of the wiring around needs: [] and references to other classes.

using needs: [‘foo’] within your controller will define a property called ‘controllers’ within the scope of your controller. You can use this system to access other controllers that may be available in the applications current state. usage is simple, just call this.get(‘controllers.foo’) to access the instance of a controller defined within needs.

ArrayControllers automatically add an arrayObserver to it’s content / model property. This means that you can define two optional hooks on the array controller through arrayWillChange() and arrayDidChange(), each of which will be passed the parameters (observedObj, first item, items removed, items added). You could easily implement a lot of simple housekeeping and mutation effects straight from that interface.

Enumerables are Incredibles
The API documentation for enumerables are here: Ember.Enumerable

Enumerables help abstract looking into and working with the contents of enumerables. Usually this just means that ember gives array controllers a big pile of functions that work quite a bit like how underscore.js does. Here’s a basic list of what you get, for free, just by using an ArrayController:

– addEnumerableObserver
– any
– compact
– contains
– enumerableContentDidChange
– enumerableContentWillChange
– every
– filter
– filterBy
– find
– findBy
– forEach
– getEach
– invoke
– isAny
– isEvery
– map
– mapBy
– nextObject
– reduce
– reject
– rejectBy
– removeEnumerableObserver
– setEach
– sortBy
– toArray
– uniq
– without

Controllers are the iron beams of your skyscraper

Take the time to go over the documentation related to controllers. Go look at discourse and their wealth of production level controllers that comprise their app.

I hope this post was helpful. Comments? Questions? Post below or ping me on twitter via @landongn