问题
I am interested in having a controller to coordinate rendering, event processing, URL router navigation and network access. A bit similar to what a Controller does in Spine: http://spinejs.com/docs/controllers
In the realm of Backbone, what I could find so far is an article by Derick Bailey: http://lostechies.com/derickbailey/2011/08/28/dont-execute-a-backbone-js-route-handler-from-your-code/
In Derick's code however, the Controller seems not to be used anymore within the Routes. Also, I was wondering, if anyone has a clearer code example, that shows the benefits of having a Controller coordinating Backbone components?
PS I am aware of the Controller in Marionette, but it would be great to see a Backbone code example without the Marionette dependency.
回答1:
First of all, I use Marionette.js, but for the controller, I use a plain object.
I divide the code for apps. So, if in my case, I have a Note taking app, I have:
- an app for taking notes
- an app to edit the categories
- an app for authentication, etc.
Within an app, i.e notes, I divide it in a RESTful way. List, Show, etc.
Taking list for example, what I have is a controller to manage this part of the app. How?
Something along the lines (you will see Marionette code, but I guess you can do it in a Backbone way, but otoh, I would really recommend Marionette):
List.Controller =
listNotes: ->
notes = App.request "notes:entities"
App.execute "when:fetched", notes, =>
@layout = @getLayoutView()
@layout.on "show", =>
@showNotes notes
@showForm notes
App.mainRegion.show @layout
showNotes: (notes) ->
notesView = @getNotesView notes
notesView.on "childview:edit:note", (iv, note) =>
App.vent.trigger "edit:note", notes, note, @layout.formRegion
@layout.listRegion.show notesView
showForm: (notes) ->
App.execute "new:note:view", @layout.formRegion, notes
getLayoutView: ->
new List.Layout
getNotesView: (notes) ->
new List.Notes
collection: notes
The controller is a plain javascript object. What it does is ask for a collection of notes (if not using marionette, you can retrieve the notes collection like you want). Then we wait until the notes has been fetched (using promises).
When we have the notes, we create a layout (you can use what you want and if you like layout idea you have layoutManager), when the layout has been shown, we show the notes list and the form (to enter new notes).
Then we add the layout to the desired region (again, layoutManager would work for non marionette user).
To show notes, we retrieve our view and we show it in the region desired to show the notes (ignore the other code).
Now the interesting part of using a controller AKA coordinating all the stuff.
We don't want the views doing any kind of business work, like add notes, remove notes... So when we click on a edit note (in the list notes view) what we do is just trigger an event (You can create your own event aggregator with one line of backbone code) and then the controller will listen for it and it will do what you need to do to edit notes. In my case, I bubble up the event to a file which manages all this RESTful controllers and then call the Edit Controller in other part of my app.
Is the same thing I do for the new note, I bubble it to another part of my app.
So, controllers, I use controllers to show the views I need for this part of the app, To do what the views needs to do which doesn't belongs to a view, like CRUD operations or transitions to other routes.
You asked about router. The good part of Marionette router (sorry, here I will talk about marionette) is that we can use a plain object to feed the router. What are the advantages here?
Imagine this:
class NotesApp.Router extends Marionette.AppRouter
appRoutes:
"": "listNotes"
API =
listNotes: ->
NotesApp.List.Controller.listNotes()
newNote: (region, notes) ->
NotesApp.New.Controller.newNote region, notes, NotesApp.categories
editNote: (region, notes, note) ->
NotesApp.Edit.Controller.editNote region, notes, note, NotesApp.categories
App.commands.setHandler "new:note:view", (region, notes) ->
API.newNote region, notes
App.vent.on "edit:note", (notes, note, region) ->
API.editNote region, notes, note
App.addInitializer ->
new NotesApp.Router
controller: API
(I deleted some code, so don't try to struggle where some params come from).
This is a router in Marionette which is initialized using that API
object. The advantage is that I can access that API
object from the router and from the other code.
So when I hit that route, I go to the listNotes
function and well, you saw what I do there. Remember how I bubbled up the edit and new views? They stop here, here I listen for those events and I just call the proper function in the API
object. The good part is I decide to create another route to go to the new form, I just need to add the route. The code is there, no need to change any code.
Well, this ended to be a giant response, but I wasn't able to explain the responsibilities I give to a controller without this :P
As final words, I highly recommend using Marionette because only provides good stuff to backbone with absolutely no drawback.
来源:https://stackoverflow.com/questions/16769087/how-to-design-a-controller-in-backbone-js