This is what I’ve learned so far.
A partial is just a way to move a snippet of handlebars into another template. (Depending on whether/how you precompile your templates, this may mean a separate file.) It’s like #include
in C. It’s literally the same as if you had copied and pasted your handlebars template. Partials, afaik, are not deprecated. They have their place; they’re just not a real abstraction.
Helpers are great when you find common patterns in your handlebars templates, or when you need a purely functional transformation of a value. When I say purely functional, I mean (1) no state, (2) no side-effects, and (3) only depends on the input parameters (and maybe context/controller). Pluralizing is the canonical example of this. Truncating and creating special links (with custom versions of linkTo
) are other examples. Because handlebars helpers support blocks, these can be more sophisticated, creating conditionals or looping constructs. But I think the key question is, is it a pattern in my template? If the answer is yes, then a helper may be the right abstraction.
On the other hand, views are required whenever you have anything impure. If the thing you’re trying to abstract has its own state, has any side-effects, depends on global state, or has any kind of user interaction or DOM event handling, then a view may be what you’re looking for. Helpers are not designed for most (if not all) of these things. In general, you have models that are persisted, and you have application state that’s stored on controllers. But sometimes you need state that’s specific to the widget itself, and this should be kept out of the controller. In general, dependencies go like this V → C → M, i.e. views can depend on controllers, and controllers can depend on models, but not the other way around. For example, if you have a widget that has some kind of active or hover state that doesn’t affect anything besides how the widget appears, it will probably make more sense to keep that state on the view. The controller should be oblivious to it (if it’s possible). It should still work, for example, if you have multiple instances of the view in the same context (i.e. using the same controller).
I think the decision to go with a view versus a component is the only real judgment call. In many cases, I could see this going either way and it’s more about taste or what you consider to be the border between your application and a library component. The differences between views and components are more subtle. You instantiate a view in your template like {{view App.MyViewName}}
but you instantiate a component like {{my-component-name}}
. This may matter. If you’re planning on reusing your abstraction between apps, a component is probably the way to go since the class name is abstracted away. There are also differences with how actions within components work. If you find yourself typing {{action 'myAction' target=view bubbles=false}}
a lot, that is a smell that may indicate you should be using a component since actions inside a component go to the component by default (not the controller) and also don’t bubble outside the component.