Backbone.js singular Models not in collection - javascript

Being new to Backbone.js, just wanted to clarify the correct way to go about this simple task.
Developing a web app, almost always, you'll have user accounts, where users can login to your app, and view their personalized data. Generally, their page might show some widgets, their user information (name, avatar, etc).
Now creating a Model per widget and grouping these in a Collection is an easy concept. However, would their user info be stored in a singular Model, which would not be apart of a Collection?
If so, how is the hooked up with the rest of the app? Forgive my ignorance, but I've yet to come across any examples that don't explain Models not using them in Collections (User and Users, Dog, Cat and Animals, etc)
Anyway, sorry for the lengthly explanation. I would be looking for any resources to get me started on making a real-world app, rather than a ToDo app (which is great for the basic concept understanding)

There is not any reason to feel forced to declare a Collection for every Model your App has. It is very common to have Models without Collection associated.
// code simplified and not tested
App.CurrentUser = Backbone.Model.extend({
url: "http://myapp.com/session.json"
});
App.CurrentUserView = Backbone.View.extend({
el: "#user-info",
render: function(){
this.$el.html( "<h1>" + this.model.get( "name" ) + "</h1>" );
}
});
var currentUser = new App.CurrentUser();
currentUser.fetch();
var currentUserView = new App.CurrentUserView()({ model: currentUser });
currentUserView.render();

If you want a model / view with no collection, don't write a collection or collection view.
Add the view the same way you normally see the collection based view added.

Related

Backbone.js View, ParentView, Model, Collection and Template Best Practices

I am looking for some description of best practices for views and models/collections in Backbone. I know how to add models to collections, render views using templates and use views in parent views, however I'm looking for more context and perhaps some example links.
I've updated this question to be more specific.
Let's say you have a more grid layout with all kinds of variation, that gets pulled from the same collection. What would you do here to create this page? A simple child view repeated in a parent view won't work because the variation of the grid items is too great. Do you:
create tons of tiny views and collections and render all of these different views using the relevant collections into that one page?
create a complex template file that has a loop in it, that as you go through the loop, the loop outputs different markup?
Do people put multiple views inside a parent view, all from the same model?
Similarly, do people mix different models into the same parent view? For example movies and tv shows - these different models, can get they added to the same collection that renders that list?
Thanks!
You've asked good question. To answer it lets take a look to this from other angle:
On my exp i used to check first is there any logic on parent view, like sorting, validation, search and so on. Second - Collection with models or just model with array as property : is the model is independent and may exist without collection , for example you have navigation item, and there are no sense to make separate model for each item and navigation as collection as you will never use item itself. Another case you have user list. You may use user model in a lot of places and its better to make a separate model for user and collection to combine it.
Your case with UL may be resolved with single model and items properties with array of li, simple grid may have same approach as i don't see some special logic on wrap from your description.
But i should point out - i had close task to build mansory grid with collection parsed from server, items were models as it had different data structure, different templates and different events listener.
Taking decision i considered folowing:
item as independent tile, may be used as in grid and also independent.
item is model + template + view. different Models types helped to support different data structure, different Views types helped to support different events listeners and user interaction, different templates - diff looks.
collection as a tool to fetch initial data + load extra items + arrange mansonry view + create models according to fetched result.
UPDATE
Lets consider this pseudo code as masnonry implementation:
Models may looks like these:
var MansonryModel = Backbone.Model.extend({
/* Common methods and properties */
}),
MansonryVideoModel = MansonryModel.extend({
defaults: {
type: 'video',
videoUrl: '#',
thumbnail: 'no-cover.jpg'
}
}),
MansonryImageModel = MansonryModel.extend({
defaults: {
type: 'image',
pictureUrl: '#',
alt: 'no-pictire'
}
});
var MansonryCollection = Backbone.Collection.extend({
model: MansonryModel
});
Views could be like this:
var MansonryTileView = Marionette.ItemView.extend({
/* place to keep some common methods and properties */
}),
MansonryVideoTile = MansonryTileView.extend({
template: '#video-tile',
events: {
'click .play': 'playVideo'
},
playVideo: function(){}
}),
MansonryImageTile = MansonryTileView.extend({
template: '#image-tile',
events: {
'click .enlarge': 'enlargePicture'
},
enlargePicture: function(){}
});
var MansonryListView = Marionette.CollectionView.extend({
childView : MansonryItem
})
Hope this help

Can I create custom methods in Backbone.collections?

Apologies I'm still learning the fundamentals of Backbone.
I'm a little confused about Backbone.collections. I understand that they consist of a group of collections, and can have url and model attributes. Can you define a custom method in a backbone.collection?
For example:
var Books = Backbone.Collection.extend({
url: '/books',
model: Book,
getBook: function(id, options){
......
}
});
Is this possible and if so, what would it do?
Thanks!
This is absolutely possible and a great way to encapsulate functionality specific to that collection. Although typically in practice I find myself expanding the functionality of the Models more.
As far as what it would do... If you had a collection of books you may want to write helper methods for accessing, pruning, or serializing your collection. I'm not sure what your business needs are.
Have look at underscore.js for available collection functions, so you don't reinvent the wheel.
Custom methods on collections would usually do one of two things:
Handle events fired by the models, if you need to trigger custom actions when that happens
Hold reusable looping/filtering/sorting so that you don't have to inline that stuff throughout your application
When I say collections would usually do this, it doesn't necessarily mean that you're doing anything wrong if your methods falls outside of these two categories, this is just to get an idea what kind of logic you might want to put into a collection custom method.
Take your books for instance, let's say you wanted to have a custom method that returns the total number of pages for all of the books in your collection. That could look something like this:
var Books = Backbone.Collection.extend({
url: '/books',
model: Book,
getTotalNumPages: function() {
var numPages = 0;
this.each(function(model) {
numPages += parseInt(model.get("pageCount"), 10);
});
return numPages;
}
});
// Elsewhere in your app
console.log("Total pages: ", books.getTotalNumPages());

Backbone: Referencing collections in the app level view

Consider the Backbone app listed below. When its started, it creates a restaurants collection and view of local restaurants in the users area. The user can later change their search area and then update the entire collection by submitting a search form.
The Problem: How can I reference the collection in the search method if it isn't related to the view?
Currently, I'm setting the restaurant collection and view as properties of the App view itself so that it can be easily referenced from within the method, but this seems in appropriate because the view isn't solely related to that collection.
Is it better to reference the collection as I have or is it better to reference it as App.collection, as backbone does when you in create views realted to collections with Backbone.View.extend({collection: mycollection})?
App view:
var App = new (Backbone.View.extend({
...
events: {
submit: "search",
},
start: function(){
App.render();
App.restaurants = new App.Collections.Restaurants();
App.restaurantsView = new App.Views.Restaurants({collection: App.restaurants});
App.restaurants.fetch({});
},
search: function(e){
e.preventDefault();
var payload = $( App.targets.searchForm ).serializeObject();
App.restaurants.fetch({data: payload, processData: true,});
},
...
}):
Those are big questions: How to structure a Backbone application, how to define and maintain module boundaries, and how to decouple modules while allowing notifications where needed, like between search and restaurants?
Big-picture answer: Look at something like Marionette. It's a framework built on top of Backbone, it makes it easy to follow a lot of best practices, and it does some heavy lifting for you. David Sulc has an excellent book about Marionette that covers all of those topics & more. Highly recommended.
Immediate-problem answer: Directly referencing objects outside of a single module (like search) is a bad idea. It couples together concerns that should be kept separate. At a minimum, think of adding an application-level event bus: search can trigger a search event, and the restaurant code can listen for that event and act on its collection in response. You could use something like the backbone.wreqr library's EventAggregator class, which is tailor-made for this kind of thing. In a pinch, you could use an instance of Backbone.Events to roll your own:
App.vent = _.extend( {}, Backbone.Events );
Trigger an event from search code like so:
App.vent.trigger('search', payload);
And in your start function, register to listen for that event:
App.vent.on('search', function(payload) {
App.restaurants.fetch({data: payload, processData: true,});
});

grab multiple instances of ID within an application

I have the below that is setup and working properly.
require(['models/profile'], function (SectionModel) {
var sectionModel = new SectionModel({id: merchantId, silent: true});
sectionModel.fetch({
success: function (data) {
$('#merchant-name').html(data.attributes.merchantName);
}
});
});
But it will only work in one instance. I am wondering how to correctly edit the above code to allow multiple instances.
<h3 id="merchant-name"></h3>
The content is generated within 'Save' function.
merchantName:$('#merchantName').val(),
What you want to do is set up the rest of the components for the Backbone application. The beauty of Backbone.js is it's ability to separate collections, models and views so your logic stays in a proper place.
You'll want to use an AJAX call to retrieve your models from the server using a Collection. Then, use the collection's reset function.
Here's an example of how you might fetch a collection of models from the server.
var MyCollectionType = Backbone.Collection.extend({
getModelsFromServer:function()
{
var me = this;
function ajaxSuccess(data, textStatus, jqXHR)
{
me.reset(data);
}
$.ajax(/* Insert the ajax params here*/);
}
});
var collectionInstance = new MyCollectionType({
model:YourModelTypeHere
});
collectionInstance.getModelsFromServer();
Then, to render each one, you'll want to make a View for each model, and a Collection View. There are a lot of resources though on learning basic Backbone.js and I feel that you might benefit from looking at a few of those.
Keep in mind that Backbone collections will by default merge models with the same id. 'id' usually references a model in the backend of an application, so make sure each id is actually what you want it to be. I work with an application that has a non-Restfull back end, and so ID's are never transferred to the front end.
There are some excellent resources available to begin starting with Backbone.js.
https://www.codeschool.com/courses/anatomy-of-backbonejs
(This is a free course up to a point, and a great starter.)
http://net.tutsplus.com/tutorials/javascript-ajax/getting-started-with-backbone-js/
http://javascriptissexy.com/learn-backbone-js-completely/

Optimal URL structure for Backbone.js and Backbone implementation

I'm developing a RESTful API for a Quiz app, which is going to be built with Backbone.js and Marionette. I'm quite new to backbone and was wondering what de best URL structure would be. I have the following resources:
Answer,
Question which contains Answers,
Question Group which contains Questions,
Quiz which contains Question Groups.
Two possible URL structures come to mind:
GET /quizzes/:id
GET /quizzes/:id/questiongroups
GET /quizzes/:id/questiongroups/:id
GET /quizzes/:id/questiongroups/:id/questions
GET /quizzes/:id/questiongroups/:id/questions/:id
GET /quizzes/:id/questiongroups/:id/questions/:id/answers
or:
GET /quizzes/:id
GET /quizzes/:id/questiongroups
GET /questiongroups/:id
GET /questiongroups/:id/questions
...
Now, I have been trying to use both of these options. With the first one, I can't figure out how to define the collections as a property of the parent models in Backbone so that I can use fetch() on them. The problem with the second option is a bit different: as I understand it, Backbone derives the url for a model from its collection, but the collection is a child of another resource, whereas the url for getting a single resource uses another collection, namely the global set of resources.
I'm pretty sure I'd have to override url() in both cases. I tried some things but didn't come up with anything useable at all. Also, I'd rather not override every single url()-model in the app, changing the API structure to suit the preferences of Backbone seems like a better option to me.
Any pointers as to what seems the right way to do it with Backbone would be great!
Thanks
If questiongroups can only appear in a single quiz, then the first option (the hierarchical one) is an obvious choice. To comply with RESTful conventions, you might want to consider using singular nouns instead: /quiz/:id/questiongroups/:id/question/:id/answer/:id
To solve your fetching problem, I would recommend using nested backbone models as per this answer: https://stackoverflow.com/a/9904874/1941552. I've also added a cheeky little parentModel attribute.
For example, your QuizModel could look something like this:
var Quiz = Backbone.Model.extend({
urlRoot: '/quiz/', // backbone appends the id automatically :)
defaults: {
title: 'My Quiz'
description: 'A quiz containing some question groups.'
},
model: {
questionGroups: QuestionGroups,
},
parse: function(response){
for(var key in this.model){
var embeddedClass = this.model[key];
var embeddedData = response[key];
response[key] = new embeddedClass(embeddedData, {
parse:true,
parentModel:this
});
}
return response;
}
});
Then, your QuestionGroups model could have the following url() function:
var QuestionGroups = Backbone.Model.extend({
// store metadata and each individual question group
url: function() {
return this.parentModel.url()+'/questiongroup/'+this.id;
}
});
Alternatively, if you don't need to store any metadata, you could use a Backbone.Collection:
var QuestionGroups = Backbone.Collection.extend({
model: QuestionGroup,
url: function() {
return this.parentModel.url()+'/questiongroup/'+this.id;
}
});
I'm afraid I haven't tested any of this, but I hope it can be useful anyway!

Categories