Ember 2.0 Controllers or Just Components...? - ember.js

I have read that Ember2 is attempting to remove controllers. I was even linked to this RFC https://github.com/ef4/rfcs/blob/routeable-components/active/0000-routeable-components.md. However, I have been following the tutorial, and they insist on making a Controller. Do we still need to make Controllers or is this out of date?

Controllers are still needed (and thus haven't been deprecated) for two reasons: query parameters, and because components aren't routable yet. You can follow the tutorial's use of controllers without it causing you too much grief later on.
However, if you want to pull ahead of the tutorial, you can use components instead, barring the two caveats above. There is no way around using controllers for query parameters, but you can avoid the lack of routable components using this simple hack:
Let's say you're creating a Route called Dashboard. The tutorial will tell you to create corresponding Controller and Template as well. Go ahead and do that, but delete the Controller. Create a component called dashboard-main, move the logic from the Controller to the component.js file and the Template to the component's Template. Then, in the Dashboard Template, just refer to the component:
{{dashboard-main items=model foo=foo bar=bar ...}}
Depending on what you're doing in the Route, you still may need the setupController() method (that's still the only way you can move values other than the model from the route to the template so that they can be passed to the component), and of course your controller/component implementation may have other minor changes, but that's the basic gist of it.
To be most ready for when controllers are deprecated, you should avoid them by using components instead.

Related

When should I use controllers vs routes in EmberJS?

I know this question might seem a little duplicate but the other version of this question is old and some of the content (such as Views) aren't even a part of ember anymore.
I'm about 4 weeks into my internship as a front-end developer working with EmberJS. I still don't understand when it's better to use the route over the controller or vice-versa. It seems to me that every action in the route can also be used in the controller.
The one recent thing I heard was that ember routes should be stateless where as controllers should be stateful.
What is the current state of controllers and routes. When should one be used over the other?
Consider the following example to understand the state of a controller (or route, or anything), in simple terms and in current context -- lets say you have a page (like a form) with three tabs; each tab can be considered as a state - it would call different components based on the state (or the tab you are in). Now if you would happen to go back for some reason, and hit the form link again, you would see that the state would remain the same. (if you were on tab 2 when you hit back, on returning to the form, you would still be on tab 2).
So to maintain these states, controllers are the way to go, since they are singletons. Route would have lost that information, and started fresh. So basically your variables/objects in a controller would define the 'state'.
Route-actions can be as easily used as controller actions- see https://github.com/DockYard/ember-route-action-helper. So if your template for this route is just using model as the object directly, and you don't need to maintain the 'state', you can pretty much do without your controller.
But if your template was using variables which needed manipulation, you would need controller.
Hope this helps!

Template inheritance in Ember

I have several unrelated pages that use Layout A, and another set of unrelated pages that use Layout B. It's as simple as that, but I can't figure out how to do this in Ember the DRY way.
I understand that template nesting is equal to route nesting, but I do not want to nest routes because it'd mean the URL will be also nested. I want to nest templates only because the pages are unrelated.
What I want to achieve is essentially template inheritance.
I expected this to work, but Ember throws an error.
// app/routes/samePage.js
import Ember from 'ember';
export default Ember.Route.extend({
renderTemplate(){
this.render('somePage', {
into: 'layoutA'
});
}
});
This is the error I get:
ember.debug.js:18015 Assertion Failed: You attempted to render into 'layoutA' but it was not found
I also get this warning. It tells me to read this link, but I don't think it helps me.
DEPRECATION: Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated. [deprecation id: ember-routing.top-level-render-helper] See http://emberjs.com/deprecations/v2.x/#toc_rendering-into-a-render-helper-that-resolves-to-an-outlet for more details.
Here is what layoutA.hbs would look like. I know you can't have {{outlet}} multiple times in a same template, but you probably get what I want to achieve.
<div class="header">
{{outlet}}
</div>
<div class="content">
{{outlet}}
</div>
<div class="footer">
{{outlet}}
</div>
How do I go about doing this in Ember? It sounds like such a basic task that needs to be more clear. Do I need to implement a template inheritance helper (like the one shown here) by myself? Or perhaps, there's already an Ember add-on for that?
Unfortunately, there is no such thing as template inheritance in Ember. But there are two features that allows to reuse html-code:
Components is well-known feature of Ember. It allows to have a re-usable template and/or js code to control such template appearance and behaviour. Component has it's own context, so all data should be passed to it via properties. I recommend to use components for custom ui elements or when you need to reuse a part of template with some logic (for example, top navigation with user menu which depends on authentication can be moved to component). There is also a trick that allows to emulate template inheritance with components using multiple {{yield}}. Maybe that's what you want.
Partials seems to be less known (they are not even mentioned in official guide for 2.x) but very useful. This helper ({{partial}}) renders any template in current context. I recommend to use it when you need to break a big template into parts.
These features are enough to reduce an amount of duplicated code. You probably can't reduce duplicated code to zero with them, but in my opinion it's not critical. Just move what you can to partials/components and your templates will be clear enough. Use that trick with component and yield if you want to emulate inheritance.
Update
Couple of words about partials
If you google "ember component vs partial", you can see a few blog posts and answers on SO, in which ppl say "don't use partials". In many cases without explaining why. The main points that I found are:
Components are more isolated, decoupled and testable. And I agree with that.
Partials may be deprecated and removed in future. However, that was first said in year 2015, but at this moment partials are still there and not deprecated.
When I suggest to use partials?
When you have a big template that is hard to maintain and you can't drop that route in parts. It happens if you use some css-framework (like bootstrap or semantic-ui) and need to implement a couple of big 3-4 step forms, or add a couple of modals or display some complex entity. Using components in this case is unneccessary (you will not use them on any other page) and their isolation adds headache (you will need to pass data that you need to display as properties and add some action(s) to get user input in case of forms).
Why I suggest to use partials in this case?
There is no need to reuse such partial, we just want to break template for easier maintenance. For example, to have each step of 3-steps form in it's own .hbs file rather than having one big template.
If partials will be deprecated, it's easy to move that pieces back to one big template.
It sounds to me like your solution is pretty simply just to use Ember components. Ember components have their own template which can nested into your route's template with {{name_of_component}}, however using components does not affect the routing.
Perhaps I am misunderstanding your need, but it seems to me like this is the easy fix. Read about it here. If I am misunderstanding the question, please clarify!
What I want to achieve is similar to using named outlets, so I can output some content into a specific place. However, it seems like Ember's recommend way to do this is to use ember-elsewhere.
The documentation says:
Before named outlets were introduced to Ember the render helper was used to declare slots for this.render in routes. This usage is not common in modern, idiomatic applications and is deprecated. In general, the pattern of named outlets or named render helpers is discouraged. Instead use of ember-elsewhere or another DOM-redirection library should better serve these use cases.
I found this add-on to be easier to understand over partials and components.
This, or using partials/components is probably the best we can get as Handlebars do not have a template inheritance feature.
Or, I should be changing the way I'm creating the app, and use routes to do template nesting. That's probably the Ember way.

Where can I find documentation on Ember's {{control}}?

I don't see a listing in the Ember API. Where can I find which parameters for this template method? Can I use it, given that it's an unfinished feature?
It is not a good idea to use {{control}} yet as it is still being developed and is very likely to change behaviour. Basically it will be like a sandboxed {{render}} which won't inherit any context. It is mentioned on this page of the emberjs guide:
{{control}} works like render, except it uses a new controller instance for every call, instead of reusing the singleton controller.
This helper is currently under heavy development, and will likely change soon.

accessing the model from the template

Playing around with ember, I found that sometimes the model is stored on the controller's content property, sometimes the model is directly available on the controller as well. I do not understand however, when this is the case.
Let me explain it by an example which I found when assembling my ember MVC.
Setup A - The start
I defined a custom Member object, corresponding MemberRoute, MemberView classes and a template with the name member.
The Member object had some attributes such as id, nickname, etc.
NOTE: no controller of the form MemberController was defined, thus by ember's convention, it provides the controller on its own.
Setup B - The customization
Same as setup A, but now there is a MemberController defined that contains some action methods that are triggered from within the template.
The strange behaviour (resp. what I do not completely understand)
in setup A, I can refer to the Member's attributes directly with {{id}} or {{nickname}}.
in setup B, I have to use {{content.id}} or {{content.nickname}}
As documented in ember's documentation, MemberView does
setupController : function(controller, member) {
controller.set('content', member);
},
So, could somebody help me to understand why the difference and where the difference is? Currently, my guess would be either
that the context of the template is different (possibly there is a code piece missing in the setup of the controller?)
or
the default controller that is provided by ember automatically, has some additional magic that is not directly avaiable for customized controllers.
Any help to understand this is highly appreciated. It already took my quite a while to come as far as this. I first thought it could be the modularization introduced by the project setup with requireJS (well, I still think that could have a influence). Ember is v1.0pre4.
Thanks in advance!
Patrick
So, could somebody help me to understand why the difference and where the difference is? Currently, my guess would be either
that the context of the template is different (possibly there is a code piece missing in the setup of the controller?)
or
the default controller that is provided by ember automatically, has some additional magic that is not directly avaiable for customized controllers.
It's hard to say for sure without seeing your code, but my best guess is that your MemberController extends Ember.Controller. The default provided by ember (in this scenario) would have been an Ember.ObjectController. If that's what you want, change your MemberController definition to:
App.MemberController = Ember.ObjectController.extend({
myProperty: 'value'
});
An objectController acts as a proxy to it's content property, typically that is an ember model. So if things are wired up correctly you should never need to access a model via the 'content` property. If you ever see something like:
{{content.id}} or {{content.nickname}}
it's a sign that you should change to an ObjectController. See EMBER GUIDES: REPRESENTING A SINGLE MODEL! for a more detailed explanation.
an ObjectController acts as proxy to the object set to the controller's content. When no controller is defined, Ember will create a controller for you and set its content by default to whatever object is returned by the model() function, if defined, in the route. The behaviour should be the same whether you define your own controller or let Ember define one for you
The default context in the template is the controller itself i.e. this = an instance of your controller or the generated one. When you try to access nickname in that context, Ember will first try to resolve it against the controller itself and if nothing is found, it resolves it against its content, i.e the object if you already manually set it to the controller's content.
Finally, there is no default implementation of the model() function in the Route except when you're using dynamic urls, say /foo/id that resolves against /foo/:id, Ember uses the id provided to load a Foo object with the id provided, thus providing a default implementation to the model() function. At the end it boils down to the same mechanism, only automated for your convenience.
I suggest you listen to this for more insights on how things are automated for you by Ember. But when it comes to the content being displayed, there is no magic you have to manually wire the content of the controller.

When to use which controller type?

I seem to be getting more and more confounded at what appears, on the surface at least to be pretty basic architectural questions regarding building ember apps.
Which Controller type?
In the last month or so, I've seen people implement controllers through Ember.Controller, Ember.ArrayController, Ember.ObjectController, and Ember.ArrayProxy. Removing ArrayController and ArrayProxy (due to them being identical), what are common use cases between each type?
So far, i've been able to gather that:
ArrayControllers/Proxies should be used when you have n elements within the view you intend to control
ObjectControllers should be used when the view is simple enough to maintain it's state in a single object, or be a single instance of a model's object.
Controllers --- ? No idea.
What are some basic differences between the controller types? There doesn't seem to be concrete information on when to use which, and for which use case. The API docs are good at telling me the nitty gritty of each of them, but not WHEN to use each.
The relationship between a View and a Controller can be baffling
When a View is connected via a routes ConnectOutlets function call, what exactly happens between the controller and the view?
Are events tied into the view itself (which seems to be the case) and if so, where on earth do you interact with the controller singleton to perform CRUD-esq things on its properties? this.get('controllerName') doesn't seem to do the trick, and nearly each post or tutorial or code sample out there does this a different way.
Models that aren't
I realize that Ember Data looks to help solve some of the more irritating parts of dealing with data and keeping it in sync, but at a larger perspective, in the concept of "MVC", ember doesn't really seem to have a Model of any kind. It's just some object that gets subclassed from a specific thing and then tracked....somewhere? Somehow? Magical?
#trek sufficed that an Ember.Object could manage ajax'ing data and handling state on the client just fine, but if you look at something like the todomvc.com ember app, it uses a localStorage paradigm that is COMPLETELY different in implementation then everything i've looked at.
How EXACTLY should the 'Model' part of the MVC equation be done here?
Views make me want to murder children
There seems to be a significant number of ways to construct a "view" in terms of displaying markup to a user.
ContainerViews, using subviews / childviews
nested outlets
Handlebars templates + an outlet
using #each foo in controller
Injection through literals (template: Ember.Handlebars.compile('<h1>foo</h1>') etc)
With that in mind, what's the 'proper' way to build modular UI components with ember? This more than anything is a major pain point for the adoption of this framework for me.
I love the direction that Ember is going with application development on the web. The concepts seem simple, but the verbosity is that of Objective-C (which makes sense given it's lineage) but I swear to god I feel like i'm fighting the god damned framework more than i'm actually working on my application. The verbosity of the syntax and the lack of structured documentation outside of API documentation (which lets face it, 300k of javascript is a significant amount of code to throw some breakpoints down and try to debug your issues).
I realize the challenge that you guys are up against, but hopefully this at least makes you pause for a minute and think of how you could make life easier for the incoming developer who's worked with other frameworks (or hell, even worked within an MVC framework, like rails or django or backbone or angular) and say "this is how we think ember should be used".
Take some of the opinionated software design decisions and apply them toward the community. We'll do nothing but be cheerleaders for you if you do it, promise.
Please don't hurt any children. AFAIK the ember-core team are all over 18, so any ember-view-related frustration is clearly better directed towards adults. With that in mind...
Which Controller Type?
You've got the "what" right, but maybe missing the "why". Controller can be a little misleading, especially coming from rails. Think of these controller singletons as representing the state (in-memory) of your application.
Which kind of controller to use depends on what is required for that part of your application. For example, a back-of-napkin sketch for any app might have a topnav, postList, postDetails section. In ember, each is represented by one or more view/controller pairs. In this app I would expect to see ApplicationController and NavigationController extending Ember.Controler while postList would extend ArrayController and PostDetails would be an ObjectController.
You could do it all using just Ember.Controller but ObjectController and ArrayController are really useful for wrapping model data. Any non-trivial ember app will probably use all three.
The relationship between a View and a Controller
A controller's job is to provide the context in which the view will be rendered. Ideally you'd like to keep logic out of views, so a typical controller will have lots of computed properties to do things like:
transform data from the underlying model objects
sort/filter/select a list of objects
reflect application state
whats the deal with connectOutlets? This is where you should be using the requested route/context to decide which views/data should be plugged into the outlets of your application. The controller's connectOutlet method has a bunch of magic to make it easy, but maybe too much magic. What happens (afaik) when you call: parentcontroller.connectOutlet 'child' is
Ember creates an instance of ChildView
The {{outlet}} handlebars helper in parentController's view is bound to this childView instance
The childView is rendered with the router.childController singleton as it's context
where to do crud stuff?: Typically in an action on the router. This seems crazy at first. Think of ember router not like rails but as a stateManager that just happens to also handle routing. In near-future router API will change to make this more clear. Anyway, use router actions to do things like create model instances, commit/rollback transactions and trigger state change. This is easy to do if you use the handlebars {{action}} helper for links/buttons as it targets the router by default.
Views on the other hand should have logic for "reacting to browser events" - that means really low-level stuff like show/hide something on mouseover or integrate with 3rd party libraries to do effects and animations.
You might find this screencast helpful in understanding how to do CRUD-esq things:
http://blog.bigbinary.com/2012/09/06/crud-application-in-emberjs.html
Models WTF?
Agreed in Ember any object could be used as a 'Model'. I think #trek does a good job of demonstrating how one might accomplish this via Ember.Object. This works great for a simple app, and six months back maybe would've been your best bet as ember-data was really immature. I'm not clear on the history of ember's todomvc app, but for sure it was written months ago. For sure it should be updated, but meantime I'd not recommend using it to learn about current ember best-practices.
Instead, you should go with ember-data. Over the last few months it has really evolved and should be the default choice for any new, non-trivial ember app. #tomdale just gave a great presentation on this topic, I'd recommend having a look: https://speakerdeck.com/tomdale/ember-data-internals
what's the 'proper' way to build modular UI components with ember?
For building modular UI components:
ContainerViews, using subviews / childviews
Injection through literals (template: Ember.Handlebars.compile ...)
For building an individual application:
nested outlets
Handlebars templates + an outlet
using #each foo in controller
Building modular UI components is a totally different problem than building an application. Ember.View and it's subclasses were designed for this purpose. You can easily extend/combine them to compose widgets with custom behaviors and share those widgets across applications.
At least that's how i've seen it done. If they are for internal use could also reference handlebars templates instead of object literals, but if planning to distribute the object literals approach seems best.
A great real-world example of this is the ember-bootstrap project. I learned a lot about working with ember-views by reading through that project's source. http://emberjs-addons.github.com/ember-bootstrap/
TLDR
Pick controller that maps to type of data being represented
Controllers provide context for the view and remember application state
Use ember data for your models
Use subclasses of Ember.View to make components
Be nice to children