Best practice for backbone.js models and collections - backbone.js

I've been learning backbone.js over the past couple of weeks and am about to start using it in anger in an app I'm writing. My question to you is about a use case for models and collections in a Bootstrap 3 navbar.
On my server side I authenticate the user and, based on their profile, assign them a role (author, editor, administrator etc.). I then construct an object that contains the appropriate menu structure for the user's role and pass that to the client using Handlebars. The intent is for the browser to construct the HTML to render the menus according to the properties (key/values) in the object using backbone.
My thoughts are that the navbar itself is a collection of models (navbar); each dropdown menu or link on the navbar is a single model (navbarItem); each of these contains a collection of menu items (navbarItemMembers), these collections being of models of each individual menu item (navbarItemMember). I can then set event listeners against each navbarItemMember to trigger an appropriate route or render action as appropriate.
So, getting to the point... am I over-complicating things? A collection containing models each containing a collection of other models, each of those mapping to a view that renders a on the main page. Seems convoluted to me, but from my (albeit limited) understanding of backbone.js it does seem the right way to do this...?
Advice much appreciated from those more experienced (battle scarred?!) than I. Thank you.

Use a collection when it's going to provide some benefit to you over a plain model. The benefits could be either
Interacting with a RESTful service where you'll want to not just get a list of data but then separately fetch/modify/add/delete individual items is that list
Defining separate Views for each item in the list rather than a having a single View that iterates over the list.
I don't think a basic navbar benefits from either of those. If your navbar has some super fancy elements beyond just links then maybe #2 applies but otherwise keep it simple, use a single model with a single view/template

Related

AngularJS two way binding with multiple select options

I have a list of fields that are not exclusive. Typically you would use html check boxes and bind each one to a Boolean value in the model. However I have been thrown a curve, to save space the users want all of the items to be presented in a select that allows multiple selections. I know how to create the select itself in HTML however I am not sure the best way to wire that up the model using the 'Angular way'. Is there a better solution than creating something in the controller to "translate" the select result to series of Booleans?
Welcome to SO!
Try using this directive:
https://github.com/amitava82/angular-multiselect
More info on SO here: AngularJS. Bootstrap multiselect without JQuery
I ended up creating a var in scope for the dropdown. Which wasn't too big of a deal as I keep my view model in a var called viewmodel, so it will not post to the server with rest of the view model. Then in my load and save functions I 'boxed' and 'unboxed' the booleans into a string array I fed to and read from the dropdown. little bit of work but it works. I was hoping the AngularJS framework could have handled this lifting for me but whatev.
Too bad you cannot data bind to an option in the select list (selected == true).

Handle Views in routing backbone js

What is the best way to switch to a different view when user navigates to a different url. In angular there is ng-view that takes care of this and inserts corresponding templates and in ember its all route based.
Is it better to just hide other views elements on routing using css or destroying other views and inserting current view?
EDIT
It would be great if someone could give an example how to re-render the view on navigating back to it again and restoring its previous state.
Eg.
if you have a check-box in a view that user can select to add some item to the cart , but in the middle he/she moves to some other url and then comes back, that check-box should be checked.
I would have a main content view with subviews and call remove on it, which is responsible for cleaning up any subviews too (calling remove on them first and going up the hierarchy tree). The concept of subviews doesn't come for free with backbone but isn't hard to implement. And finally attach a new content view.
This ensures you can cleanup and the browser is using a consistent amount of resources.
I would abstract this into some kind of layout view which has a content subview and a function like setContent(view) which handles the remove of any existing content view and the attach of the new one.
Personally I would have a router with sub routers in modules, e.g. a main router which finds a route starting with "checkout" and passes it over to a sub router in the checkout module which is responsible for attaching a new content view.
In Backbone the implementation is up to you which is both good and bad, depending on how nice you do it ;)
Always remove the view as opposed to just hiding it. If you don't remove (and unbind) your views properly, all bindings, handlers and references to models/DOM elements will linger around.
Depending on the size of your app, you can have a module that handles layouts (as suggested by dominic-tobias), or have a method on the router that takes care of this for you. At its most basic, this method (let's call it _switchView) takes a view and holds onto an instance of the currentView. Upon view change, it removes the current view, sets the new view to the current view and then renders it to the DOM.
Something like this:
_switchView(view) {
this.currentView && this.currentView.remove();
this.currentView = view;
this.$rootEl.html(view.render().$el);
}

rendering an AngularJS detail view within list view

I'm a little new to the AngularJS world, and I'm wondering what the best way is to show a resource detail view within a list view.
Given a path like /people, I would just show a list of people resources. If I were to navigate from one of those people to a url like people/john-doe, I can easily get a detail view and all the data associated with that person. But how can I do this without leaving the list view? I'd like to somehow append the detail to the list, or show it within the list, etc.
What you're describing is nested views, which isn't something that the regular ngRoute library does well. I advise you check out angular-ui router.

How to append new model to view in backbone.js?

On adding new model to my collection I don't want the view to be refreshed but I want the model to be appended to my collection and be appended to the view. SO i shouldn't render the view again. Is it possible via Backbone.js? How should I proceed ?
You could have a view that handles the whole layout, and a view that represents a single model. You would make the render method of the latter to return the HTML of a single model, and append the result to a list using the main view. You have a great example of this in the Addy Osmani's book "Developing Backbone.js applications". Take a look at the section where he explains how he renders each task to the todo list of his Todo App, if I understood well, your problem is solved in there.

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 could 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.

Resources