Get Started with Profiling Ember.js – Ember.js SF Meetup

I recently was up in SF giving a talk related to performance for the ember developer.

Some things I should cover in a following talk:
– Deep Dive on Ember perf for internals
– Strategies for deferring rendering
– Handling Glimmer updates efficiently
– How to paint less
– how to composite less
– how not to block your UI

Ember Components, Controllers, and the Interface Model

I write lots of ember components.  Most components have a certain amount of interop with the controller that is presenting it. How you manage that coupling makes all the difference, though.

There’s two schools of thought for components.  The idiomatic way, as presented in the ember guides, would be to have a single action for a component that is bound to an action or method on the presenting controller.  This style of design lets you work on having a component to one thing well, though it has a few limitations.  For starters, if you’re building a component because there’s (almost) no reason to use a view, a single action and some bound properties doesn’t make life easy for having a robust component.  Second, most UI widgets I end up building do quite a few things in the context of itself based upon the state of the controller.

The second way, and the way I’ve been working lately, is to pass the presenting context as a property called “ctx” to the component.  This allows me to use any method, property, or computed on the controller in my component without having to explicitly bind each value.  You could argue that you’re saving on setter/getter calls due to fetching directly from the controller.  You could also argue this is handy because it allows a controller to mixin support for a style of component.  Hey — that means we get interfaces! in javascript!

Lets say we have the following bits:

This looks wacky, but there’s a sort of mad scientist vibe happening here.  We’ve been able to define a discreet bit of functionality within a mixin that any controller can use to support a component.

To me, this looks like a better model than the one-action-multiple-bindings approach that the guides provide. You have the flexibility to provide action bubbling by invoking actions on a controller, which eliminates the need for a large implementation at the component level.

This all comes with one giant disclaimer — this approach works because it requires discipline.  If this looks like it’s a lot of extra work– that’s because it is. And that’s okay, because we’re doing something wacky with a pattern that doesn’t technically exist in the language we’re working in.  Most of the time, I’d recommend you build toward the best case – which is standalone, loosely coupled components that don’t work with the controller much at all.

But, for those moments where that’s not enough, and you’re about to build a view, this is the best alternative I’ve been able to find.

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

 



Things I wish someone had told me when I was learning Ember.js

This article is really old and shouldn’t be used anymore. Check out the ember guides instead.

  1. Learn the nuances between a route and a resource.  A resource is a definition of a thing. a route is something that thing can do. Nesting resources just means that you may have a dependency or parent / child relationship.
  2. If something doesn’t need to interact with any other controller, view, or component, it should be a component to maintain your sanity.
  3. Make a /packages/ directory, one for each namespace.  Grouping files by name helps enforce the naming conventions that are all over the place.  Do the same with your sources.  e.g; /packages/application/applicationRoute.js
  4. follow the [namespace][Type] naming convention, so that you can super+p any file by typing it’s namespace. e.g; /packages/application/applicationView.js.  The only exceptions are components due to limitations of the framework. (expects a components folder and a templates/components folder).
  5. Don’t use AMD, CJS, or browserify.  This forces you to work within the conventions of the framework, as all references to other namespaces are available within your declarations in nearly all scenarios (controllers can use needs: [], route actions bubble, view’s have ViewTargetActionSupport mixins available, etc.)  I personally use grunt-neuter.
  6. Think declaratively and reactively about how you want your logic to flow.  An example of this would be defining a ‘currentIndex’ property, and binding to when that index changes via observers or computed properties.
  7. Avoid pub/sub and Ember.Evented as much as you can.  It’s tempting to go the easy route with pub/sub to communicate with other components.  It’s almost universally a bad idea to do this, since there are so many ways to communicate with other parts of your application.
  8. avoid any logic within your templates.  They should be focused strictly on presentation.  Create computed properties on your controllers for defining the state of a view, and use those in if statements if you absolutely must.
  9. don’t use the {{each}} syntactic sugar.  use {{each foo in object}}.
  10. Long names are okay.  You shouldn’t be worried about using computedCurrentIndex vs idx in your definitions.  Think of the other developers who might someday look at this.  Do be that guy who tries to save a few bytes in javascript because they want to eliminate characters.  If you’re not using Uglify already in your grunt toolchain, What the hell is wrong with you?
  11. Treat your javascript as annotated source code, rather than something that gets interpreted.  What gets spit out of uglify doesn’t really impact any of your compiled code; go hog wild.
  12. components are effectively a controller, view, and template without a specific route.
  13. learn how to use the {{render}}, {{#view}} and {{view}} handlebars helpers.   {{render}} is invaluable for partials that may or may not need state. {{#view}} lets you define a yield block in your template or define it inline if need be.   {{view}} can be used for any self contained views you may need to otherwise {{render}} as a partial.
  14. Avoid Ember.Containeras much as you can.  If something cannot be accurately represented within an Ember.Object, try again.  The API is ugly and frankly Ember.Objects give you just about all of the value of them, short of their singleton-like nature (which you can manage with an IIFE and an instance if you must).
  15. this.send('someActionName') within the context of an any controller or route will trigger that action on the controller first, and will bubble.  Use this to your advantage wherever possible.  Need to communicate with routes to a parent of your own? this.send('action') all day long.  Need to be able to override actions in a specific controller or route? just return false from the route, or return true to continue propagation.
  16. jQuery events, short of very specific events or library integrations, should be eliminated in favor or route/controller actions, or handled by a view directly.  This will save you from having to fight the framework later on when you start running into edge cases, dangling references, or worse; zombie events.
  17. Initializers are to be abused whenever possible.  Use them with aplomb. App.Initializer('namespace', initialize: function () {}); – learn what App.delayReadiness() and App.advanceReadiness() are (iOS developer? this is a retain/release cycle for your application’s boot up.  once the retain cycle hits zero, your app is going to start up. Want fixtures? Load them via an initializer and inject them into an Ember.Namespace).
  18. Mixins are great for abstracting common things.  The easiest example would be defining a keyboard event as a mixin, then just executing methods on a “keystrokes” hash in your mixed-in class.   Voila- now every controller or route or what have you can support specific keyboard combinations.   Need to handle pagination? make a consistent mixin api and use it across the application. These are the towels that make you DRY. (groan)
  19. if you find yourself using this._super() pretty much anywhere other than in route.init() or controller.init(), ask yourself long and hard if that inheritance chain can’t be handled in a mixin.  You can probably refactor that for the sanity of everyone.
  20. within the context of a route you can access methods such as this.modelFor('namespace') or this.controllerFor('namespace').  If those elements are currently active, they will return their models or controllers.  Don’t make extra http requests if you can avoid it.
  21. make sure that within a route’s setupController hook, that you set the model on the controller before doing ANYTHING else in that hook. e.g; controller.set('model', model);.  Nothing else will work and it’ll throw an error otherwise. You may see model and content interchangeably used. Use model, not content, it’s getting depreciated.
  22. when you define a resource within your router, you’ll get route.index, route.error, route.loading routes for free, regardless of if you define them (you ought to, especially index).
  23. resources defined in your router have two layers; the base resource (defined as whatever your namespace is + route, controller, or view.) will be loaded FIRST, and then any sub-routes of that resource are loaded.  This means that an App.FooRoute, App.FooController, and App.FooView are loaded before App.FooIndexRoute and App.FooIndexController are set up.  use this.modelFor('foo') to grab the parent’s model if you need data.  Your separation of concerns here is that you want to have the parent resource manage it’s model, and any routes defined under it take care of performing actions with that model.  This one took me ages.
  24. Need to handle view transitions, in the visual sense?  Define a mixin that implements two methods: didInsertElement (be sure to call this._super()!), which in turn calls didAppear.  Each view’s didAppear hook can then set up any transitional properties necessary to animate the view appearing.
  25. in the vast majority of cases, you’ll be binding a lot of classes.  learn the semantics of the {{bind-attr}} helper when it relates to classes.
  26. figure out a good way to bind properties to inline-styles.  You may balk at this, but what do you think jquery.transit, TWEEN, and jquery.animate() do?  now imagine you could databind to those animation flags, using rAF.  https://github.com/yderidde/bindstyle-ember-helper
  27. when using a component in a template, remember that the assignment is {{my-component INTERNAL_BINDING=EXTERNAL_BINDING}}.  Don’t be afraid to pass in an entire controller’s context if that component is complex.
  28. on that note, don’t be afraid to nest components into one another.  There’s no reason why you shouldn’t do that.

 

This list is by no means exhaustive, but it’s a good starting point of little tricks i’ve picked up along the way. Have one to add? leave a comment or ping me on twitter @landongn