Why do controllers exist?

I’m new to ember and I’m just befuddled about controllers, and why they are separate from routes? Was this a necessity in the design of ember or is it just the heavy hand of opinionated software?

If I proceeded to build a massive ember app, at what point would I have to use a controller?

1 Like

With the caveat that controllers are going away in like 2 months in favor of routeable components, here’s the distinction:

The route catches a URL, grabs the model for that route, does any necessary setup work, attaches the model as a property on the controller, and renders the view, which has a template. Any short-term state or bindings all happen on the controller. When your template has something like <p>{{someText}}</p>, it’s looking up someText on the controller. Ditto for any conditionals on your template, and any actions you fire on a template will look for a handler on the controller first.

Controllers were pretty much all-stars in Ember 1.x. There’s a few reasons they’re going away, but the reality is that not that much is actually changing. Components have everything controllers have, plus everything views have. Different name and concept, but they will end up serving the same function that controllers and views do now.

2 Likes

No wonder why I was so suspicious of controllers. They just had that will be refactored out feel to them.

Couldn’t you do this all with routes? I was a little dismayed recently when I put some actions in my route and they worked just fine. I thought I had finally understood controllers, and yet again I was wrong.

You actually can’t do much of that with routes, let alone all of it. Most importantly, your template has no knowledge of your route, and thus cannot bind to anything but a controller property. The setupController hook on the route sets the result of the model hook as a property on the controller. When your route handles an action, it’s because the controller bubbled it. All of the lifecycle hooks, like init, didInsertElement, willDestroyElement, etc. are either on the view or controller, and thus where most of your third-party plug-ins get initialized and torn down. Almost all of your business logic will generally go in the controller.

Routes are Ember objects, and thus very flexible. That said, they’re primarily for grabbing data and handling transitions. It can be useful to handle actions on a route if they involve either of those two things or if you need to trigger something up the hierarchy, but they’re not really a great place for most of your logic. None of that changes in 2.0. The big difference is that controllers and views are getting mushed together and “proxying” is going away. Proxying is probably why you didn’t realize that your template was backed by your controller, not your model. ObjectControllers and ArrayControllers let you bind with a simplified syntax ({{propertyName}}) instead of what it actually is ({{model.propertyName}}).

This is all informative, but does it explain the why of it? You are telling me that routes do this, and controllers do that, but was that a rational design decision? Why aren’t routes and controllers mashed together into one thing?

Those decisions happened well before my time, but I agree with them. If anyone with more insight wants to contradict me feel free, but to me it comes down to state and lifetime:

Routes are stateless. Models have state that persists between sessions. Traditional controllers have state that persists throughout the session (these are/will be services). Views have state that lives as long as they’re displayed (these are/will be routeable components). Each of them do something a little different, and how long a particular piece of data needs to live can be a clue as to where it should go.

Controllers could have been mashed together with routes, but it would have made your code a lot less cohesive. The same object would hold your data access logic, instructions for your error and loading states, your computed properties, your template actions, your observers, your business logic…

3 Likes

This is extremely helpful and should be at the top of the guides, rather than GoF babble like “In Ember.js, controllers allow you to decorate your models with display logic.”

Looks to me like a controller is just a place a template puts its state and brains, since it can’t do that stuff with html.

And sometimes, when some id changes in the url, new info comes down in to that controller through the routes model, which the template will consume without knowing there’s even a route up there.

I suppose, architecturally, controllers could have just been combined with templates in some unsightly way, where the state and brains are puts in tags, or maybe in some cleaner way… umm… routable components right?

Here is my current take on controllers and data down actions up and routers,

The controller is a place for the template to access and manipulate its view state and also a place to put it’s view logic. The controller should not directly manipulating its passed in model or other external models except in the case of things like inputs where two way binding is default. Pretty much anything else is handled in the router via actions, that way you have data down actions up.

Here is a somewhat complex real world example to try and illustrate the approach and it’s caveats.

Enter route

The route passes in a newly created session model to the controller. The model has email, password.

User can put in email or password

When the user puts in the email or password the data should update the model via a two way binding with the input. In future as stated by the 2.0 rfc

It also eliminates the boilerplate associated with an event-based style when working with form controls. Instead of copying state out of a model, listening for callbacks, and updating the model, the input helper can be given an explicit mutable binding.

<input value={{mut firstName}}> <input value={{mut lastName}}>

User can click the login button

On login the controller can do a quick validation check on the model and make sure that the email is a valid email / the password is the minimum length and update it’s view state to include isValid? and the errors hash which contains failing fields. The template can then display a warning to the user stating that their email is invalid / password length is incorrect.

Assuming the user has passed validation the controller should sendAction(‘login’) to the route. You could include the model too. The router then calls the save function on the model.

If the save fails because of incorrect username and password then the model will now have isError? true and the template will update to display the errors from the models errors object.

If the model save succeeds then the router will transition to the default route or attempt to resume the transition that was interrupted by login.

User can click the sign in button

The template can have an {{action "sign-up"}} on the sign-up button that doesn’t even need a handler as the action will bubble to the router where it will call this.transitionTo("sign-up") or whatever your sign up path is.

** if you are doing more complex authentication such as token authentication then it may make sense to break out authentication into it’s own service. This is getting into grayer area on how exactly to handle this.

In ember simple auth this is done by calling the service directly to authenticate and then handling the promise to set errors / isError. I believe the transition still occurs in the router as the router listens on the service for authentication and other events.

To me this is a case of an action going the wrong direction since I think of the service as being at a higher level as compared to the route. I would do it a little different, instead of having the service send events I would have the service take a session model and change the session models state to isError? with errors or isSaving? so that the template can update based on the model passed in.

Calling the service directly from the controller is also another gray area as it’s not really sending an action, BUT because the service could be viewed as the top level, calling the service directly is basically just short circuiting an event bubbling up to the service and being handled there anyways. This is especially desirable if you have deeply nested components. From a purist data down actions up perspective I think this violates the principle. It also has some disadvantages in terms of moving your components around IF the components event can be interpreted differently in different contexts. For instance a close event on a modal dialog may cause the dialog to just close in one case but in another it may also undo model state changes in which case having a call to a service directly to undo state changes would be undesirably.

Conclusion

This is my current take on it and others may / will disagree on parts of this but it’s working well for me so far. I also emphasize current because my understanding of ember and ember itself have been changing pretty rapidly.

1 Like

Not disagreeing with you, but I find this idea curious. Back in prehistoric times (yeah, I started with Sproutcore) there was a recognition that JS applications in the browser, unlike “traditional” web applications, had real state and would benefit from a defined statechart. (This is a revolutionary concept for most web developers, but apparently desktop coders worked with statecharts for decades before everyone started trying to fake state on top of stateless http.)

The early revs of Ember (like, in the 0.6-ish times) had a statechart, but sometime around 0.9 that evolved into routes, and it’s seemed to me that the idea of a statechart has been pushed further and further into the background since then. (There was some intense debate around this at the time, which I’ve largely forgotten because I’m on the old side for a web dev, and I’m sure if Tom or Yehuda were asked about this they’d roll their eyes and say, “I thought we resolved this years ago,” and they’d probably be right. I still think Ember devs would benefit from studying the ideas behind statecharts.)

tl;dr it seems funny to me that routes are stateless given that they used to codify state.

@flashesofpanic I think it got moved here https://github.com/emberjs/ember-state. I remember bumping into state stuff when I was digging into transitions in the router so I think it may still be a finite state machine that manages all the routes.

Interesting… last activity over a year ago. :unamused:

The state itself goes into the URL. Routes serialize state into URLs and deserialize state from URLs. The route objects themselves are stateless.

In Ember 1.x there are actually some ways that route objects are not completely stateless. Those are being corrected in 2.0 to simplify things. See this part of the routable components RFC.

The original concept for Ember’s Controllers came from Cocoa (via Sproutcore). If you read up on what Controllers do in Cocoa it may help clarify.

Controllers are indeed slated to go away, but as of this moment you still can’t quite avoid them entirely, because the newer alternative (routing directly to a component) is not ready. But if you’re writing new code right now and you find yourself doing anything non-trivial in a controller, it’s fairly straightforward to just push the work down into a component where it will be more future-proof.

I’m pretty new to Ember/JS frameworks world, so I have a basic question about this new way of ember 2.0:

If I have a property that controls a interface behavior but it’s not in the model, it should be in the component ?
So, basically the component will observe his own properties or use the bindings to control his behavior ?

Yep! Things that used to be in the controller or view will now both be in the component.

Well, sounds really much more simple. Thx @kylecoberly :smile:

Out of curiosity what is the end all goal with moving to routable components. It just seems to me like the view and the view code are just becoming more tightly coupled.

I come from an asp.net world and these routable components sound a hell of a lot like aspx/code behinds, or more precisely user controls. In this concept an interface template is designated and then a corresponding control code file sits right next to it. The code control file is often very, very tightly coupled with the view and does data manipulations as needed to display the passed in data. (Or gets its own data) Because of this the page the control is loaded on also becomes tingly coupled with the control as the control will enivietably Fire events upward. Eventually you end up having too much business logic in code behinds because they have too much power and the whole thing becomes a hot mess of wires. Asp.net went away from that structure a few years ago in favor of strict MVC specifically to avoid issues of tight coupling.

I understand reducing complexity is a good thing and honestly I’m used to the idea that I can fire an action and then directly manpiulate the ui to suite my needs but from my perspective this seems like a possible minefield that ember will need to tread carefully through. I have no idea if this concept will work in ember where it failed in asp.net considering it’s all running on the client. It very well might work out. Who knows.

1 Like

@Kilowhisky If you can slog through wall of comments in the RFC, you’ll see that I made that very argument. I’m still not entirely happy about it, but I do understand the reasoning.

Having hung out on this forum, the IRC channel, meetups, etc. for around a year now, a reasoned understanding of controllers, views, and templates is probably single biggest barrier to grokking Ember right now- even more so than CLI or dev tooling. A lot of people never actually understood how the template binding worked, but the proxies made them think they did, which is a dangerous and eventually frustrating place to be. Ditto for not understanding the role of view objects. Once you get those concepts, real astronaut stuff like the run loop and data adapters are much less daunting.

Everybody gets components almost instantly, and the actual difference between using a view/controller vs. a component is almost trivial. So, we bridge the gap with routable components. This also allows a template’s state to reset when it goes away, which is how I think most people assume it should work.

Yes, it couples code and leaves room for people to make doofy architecture decisions that even an opinionated framework won’t save them from. The reality is that also happens now, but when the framework pushes back at them they just get frustrated, quit, and say Ember is impossible to learn. Sacrificing a tiny bit of architectural integrity to allow a ton of people to be a ton more productive isn’t an unreasonable trade.

1 Like

This is all informative, but does it explain the why of it?

The existence of The Controller comes from the MVC design pattern. Just as said in one of the comments above:

The original concept for Ember’s Controllers came from Cocoa (via Sproutcore).

Cocoa evolved from NeXTstep which was built based on the classic MVC design. You can see a timeline here: http://mvc.givan.se/

I think you should say the name “controller” comes from the MVC pattern. I’ve worked on many MVC frameworks - they had real controllers, they didn’t look much like what ember calls a controller. Just the fact that ember controllers can get refactored out is telling because I don’t see how you could refactor real controllers from a MVC framework.