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.