Backbone.js Approach to Managing UI State / Handling Selections in UI
My question deals with this UI sample.
Having trouble with approach to managing the "selected" state of various UI view components. For example, I have menus above from which the user makes various selections. These selections should cause updates in the menus themselves (HL selected items) and also cause updates in the results, which would be based on the selections made. Also, the menus have different kinds of rules. For example, you can only have one "list" selected at a time, but you can have multiple "tags" selected.
One approach that I was thinking about was to create a Backbone model that holds the state of the UI "selection". For example, I could have a model SearchCriteria that holds this information. Then, when a user makes choices in the UI, I could update this model. I coul开发者_Python百科d have the various view components listen for changes in this model (as well as changes in the primary data models.) Then, the views would update their visual state by updating which items are shown as selected.
One item I am struggling with in this approach is who should be responsible for updating the selected state of an item. For example, on the list of tags, I might have the following pieces defined...
- Tag (model to represent a tag)
- TagCollection (collection to represent a collection of tags)
- TagMenuView (view that represents the menu of tags available to select)
- TagMenuItemView (view that represents a single item in the menu)
Should I...
- Set up an event listener on the TagMenuItemView for click, and then try to handle 1) updating the SearchCriteria model, and 2) updating the visual state of the menu, e.g. selected items?
- Or, should I have the higher level view (the TagMenuView) listen for events such as the user selecting a tag, and perform the work there?
- Also, the tags menu in this example allows multiple items to be selected, but the lists menu would only allow one list at a time to be selected. Where would this "UI" rule (or is this really a business rule related to a search?) be enforced? For example, if I listened for click events on each individual list menu item, I could certainly update the visual state of that item, but, I also need to make sure the higher level menu view deselects any other selected lists. So, would it be better to manage the "UI" state of something like the to-do list menu in the view that would represent that entire menu (a ToDoListMenuView) rather than on each individual menu item view?
Sorry for so many questions. I am just having a hard time moving to this model of development.
You're almost to an answer similar to the one I would use.
If you appreciate that list
, search
, due
and tags
are search filters on a big collection of to-dos, you are 90% of the way to enlightenment. In fact, other than search
, all of those are just "kinds of tags"! (Unless you have 10,000 to-do items, there are no performance or memory-related reasons to have lists of lists of to-dos; "Work", "Project #1", and "Personal" are just specialized tags by which you filter items out of your view, showing only those related to one sphere of your life or another.)
Create the SearchCriteria model. (You are not technically searching, you're filtering: you're excluding from your view those things that don't match your search criteria.) This model will hold a lot of attributes about your client state. Your search criteria are almost entirely driven by data present in the client (since the search applies to only one ToDoList at a time), so it's entirely SearchCriteria related, not ToDo object related.
All Views bind to change/add/remove events on SearchCriteria. When the user clicks on any of the views (list, view, tag), that message is forwarded to SearchCriteria. It makes the appropriate internal changes, which in turn triggers the views to re-render themselves. One of the event recipients in the main ToDoListView, which during its render then checks the search criteria. Something like:
ToDoListView = Backbone.View.extend({
...
render: function() {
var self = this,
toDraw = this.collection.filter(
function(c) { return this.searchCriteria.passes(c); });
$(this.el).html('');
_.each(toDraw, function(c) {
(new ToDoItemView({model: c, parent: self})).render(); });
}
That may be a little personally idiomatic, passing in the parent object and letting the item insert itself into the parent's DOM object. You could pass in anything: the element to be appended to. Alternatively, render could return a DOM object and the ListView could do the appending. That's a matter of taste. I've done both.
You do have to dig a little into backbone's parent library, underscore, to grok the essential wonderfulness of the _.each()
usage.
Also, I've often contained an entire Backbone application in a self-executing anonymous function, and leaving "searchCriteria" as a variable accessible to all objects within the scope of the SEAF, so it wouldn't be this.searchCriteria
, but just searchCriteria
.
You can also write SearchCriteria so it calls sync, writing event state to the server, which you can then save as a raw JSON object; the nice thing about sync is that if what you send and what you receive are the same, no events are triggered, so you don't get a double-render effect, and the nice thing about using JSON is that it's client-appropriate, but contains nothing that the server's ToDo relationships care about.
Furthermore, you can specify specific client-side behavior rules. Such as: when you change ToDo Lists, you can apply the text-search criteria, or, as an alternative, you can decide that changing lists clears the text-search criteria field; doing so will trigger an event that will cause the "TextSearchView" to clear its input box (you'll have to write that event handler, but it'll be obvious you meant to do that). You can make up any rule you like, such as "changing lists clears all selections," but that doesn't seem sensible. I can easily imagine trying to tackle the bugs in my "project" list and in my personal life. But clearing the search box just seemed more... sensible, if you know what I mean.
精彩评论