Collection of unrelated Models in Backbone? - backbone.js

I am creating a single page app that lets users filter for data upon two criteria (Skills and Location). This data is to be populated from two separate web services.
There is a model for each service to consume the data using REST style requests.
I want to use both bits of data in this one view. From my understanding a collection can hold multiple instances of one type of Model e.g. "Movie"
var Movies = Backbone.Collection.extend({
model: Movie,
initialize: function() {
console.log("");
console.log("Movie Collection initialize");
console.log(this);
console.log(this.length);
console.log(this.models);
}
});
var movie1 = new Movie({
"title": "Bag It",
"averageUserRating": 4.6,
"yearReleased": 2010,
"mpaaRating": "R"
});
var movie2 = new Movie({
"title": "Lost Boy: The Next Chapter",
"averageUserRating": 4.6,
"yearReleased": 2009,
"mpaaRating": "PG-13"
});
However I am trying to implement the pattern below, where the collection has two Models. Is this an anti pattern for Backbone. How should this be tackled?
define([
'underscore',
'backbone',
'models/locationsModel',
'models/skillsModel'
], function (_, Backbone, Location, Skills)
{
'use strict';
var FiltersCollection = Backbone.Collection.extend({
// The filters collection requires these two models that will provide data to the filters view
location: new Location(),
skills: new Skills(),
initialize: function() {
//Do stuff
}
});
return new FiltersCollection();
});

I can't advise on what is best for you because I can't visualise your data properly based on the info provided. But if you observe the collection constructor in the Backbone source:
if (options.model) this.model = options.model;
Then in _prepareModel:
var model = new this.model(attrs, options);
And we knew that "model" is a function anyway, and a function can return what you want. So providing your two different data sources have some attribute that can identify them you can do something like this:
var SkillModel = Backbone.Model.extend({
sayMyName: function() {
return 'I am a skill model and I am skilled at ' + this.get('name');
}
});
var LocationModel = Backbone.Model.extend({
sayMyName: function() {
return 'I am a location model and I am relaxing in ' + this.get('name');
}
});
function FilterModel(attrs, options) {
if (attrs.type === 'skill') {
return new SkillModel(attrs, options);
} else if (attrs.type === 'location') {
return new LocationModel(attrs, options);
}
}
var FilterCollection = Backbone.Collection.extend({
model: FilterModel
});
var filteredCollection = new FilterCollection([{
type: 'skill',
name: 'carpentry'
}, {
type: 'location',
name: 'India'
}, {
type: 'skill',
name: 'plumbing'
}]);
var outputEl = document.querySelector('#output');
filteredCollection.each(function(model) {
outputEl.innerHTML += '<p>' + model.sayMyName() + '<p>';
});
<script src="http://underscorejs.org/underscore.js"></script>
<script src="http://backbonejs.org/backbone.js"></script>
<div id="output"></div>

Related

Backbone.js Uncaught ReferenceError: x is not defined

I am getting Uncaught ReferenceError: _auditNumber is not defined error while trying to bind my model to the view using backbone.js and underscore.js
<script id="searchTemplate" type="text/template">
<div class="span4">
<p>"<%= _auditNumber %>"</p>
</div>
<div class="span4">
<p>"<%= _aic %>"</p>
</script>
Collection
//Collection
var AuditsCollection = Backbone.Collection.extend({
initialize: function() {
this.on('add', this.render);
},
render: function() {
_.each(this.models, function (item) {
var _auditView = new AuditView({
model: item
});
$("#audits").append(_auditView.render().el);
});
},
});
Model
var Audit = Backbone.Model.extend({
url: function () {
return myUrl;
},
defaults: {
_auditNumber: "",
_aic: "",
},
parse: function (data) {
data.forEach(function (auditItem) {
var auditsCollection = new AuditsCollection();
auditsCollection.add(JSON.stringify(auditItem));
});
}
});
// Sub View
var AuditView = Backbone.View.extend({
className: 'row-fluid',
template: $("#searchTemplate").html(),
render: function () {
var tmpl = _.template(this.template);
this.$el.html(tmpl(this.model.toJSON()));
return this;
}
});
I know I am missing something simple, any help is appreciated.
2 problems (at least - you're kind of off in the weeds given how many backbone tutorials there are).
Your model URL is returning a list of results. That's what collections are for. Your model should fetch a single record and the parse method has to return the model's attribute data. If you stick with the tutorials, you won't need a custom url function and you won't need a custom parse function at all.
var Audit = Backbone.Model.extend({
url: function () {
//This needs to be a url like /audits/42 for a single record
return myUrl;
},
defaults: {
_auditNumber: "",
_aic: "",
},
parse: function (data) {
//this needs to return an object
return data[0];
}
});
You aren't passing a valid data object to your template function.
// Sub View
var AuditView = Backbone.View.extend({
className: 'row-fluid',
//compile template string into function once
template: _.template($("#searchTemplate").html()),
render: function () {
//render template into unique HTML each time
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});

Backbone JS Collection not being populated

Just started using backbone and all was going good until I tried load in some Json data.
// Category Model
var Category = Backbone.Model.extend({
defaults: {
title: "Not specfied",
paramName: "not_specified",
filter: false
},
initialize: function(){
console.log("Cateogory Model Initialized");
}
});
// Category Collection
var CategoryList = Backbone.Collection.extend({
url: '/assets/js/libs/items.json',
parse: function(resp, xhr) {
return resp.facets;
},
model: Category,
initialize : function() {
console.log('This Collection has been called');
}
});
var categories = new CategoryList();
categories.fetch();
If i try to look at any of the items in the collection it is empty !
My json file looks like the following
{
"facets": [
{
"title": "Military Service & Conflict",
"searchParam": "Military"
},
{
"title":"Census, land & substitutes",
"searchParam":"Census"
},
{
"title":"Education & work",
"searchParam":"Education"
}
]
}
If I can get this working the I just need to sort my view out
Rob

Backbone/Underscore uniqueId() Odd Numbers

I'm relatively new to Backbone and Underscore and have one of those questions that's not really an issue - just bugging me out of curiosity.
I built a very simple app that allows you to add and remove models within a collection and renders them in the browser. It also has the ability to console.log the collection (so I can see my collection).
Here's the weird thing: the ID's being generated are 1,3,5... and so on. Is there a reason specific to my code, or something to do with BB/US?
Here's a working Fiddle: http://jsfiddle.net/ptagp/
And the code:
App = (function(){
var AppModel = Backbone.Model.extend({
defaults: {
id: null,
item: null
}
});
var AppCollection = Backbone.Collection.extend({
model: AppModel
});
var AppView = Backbone.View.extend({
el: $('#app'),
newfield: $('#new-item'),
initialize: function(){
this.el = $(this.el);
},
events: {
'click #add-new': 'addItem',
'click .remove-item': 'removeItem',
'click #print-collection': 'printCollection'
},
template: $('#item-template').html(),
render: function(model){
var templ = _.template(this.template);
this.el.append(templ({
id: model.get('id'),
item: model.get('item')
}));
},
addItem: function(){
var NewModel = new AppModel({
id: _.uniqueId(),
item: this.newfield.val()
});
this.collection.add(NewModel);
this.render(NewModel);
},
removeItem: function(e){
var id = this.$(e.currentTarget).parent('div').data('id');
var model = this.collection.get(id);
this.collection.remove(model);
$(e.target).parent('div').remove();
},
printCollection: function(){
this.collection.each(function(model){
console.log(model.get('id')+': '+model.get('item'));
});
}
});
return {
start: function(){
new AppView({
collection: new AppCollection()
});
}
};
});
$(function(){ new App().start(); });
if you look in the backbone.js source code you'll notice that _.uniqueId is used to set a model's cid:
https://github.com/documentcloud/backbone/blob/master/backbone.js#L194
that means that every time you create a model instance, _.uniqueId() is invoked.
that's what causing it to increment twice.

backbone model variable only populating when I step into method

The situation I'm experiencing is that my fetch is working fine, I can see the data in my backbone collection, if I step into the method where I assign the item from the collection to the model var or pause for a second or two, all is well: the line of code this.member gets populated
this.member = this.members.get(1);
. If I just let the code run, I wind up passing in a null model to my view. I don't understand what I'm missing. Do I need to bind data in the collection before I access the collection? I'm trying to fall in love with Backbone, but so far, she's been a cruel mistress...
`
//create the namespace
var Endeavor = {
Models: {},
Collections: {},
Views: {},
Templates: {}
};
Endeavor.Models.Member = Backbone.Model.extend({
idAttribute: "Id"
});
Endeavor.Collections.Members = Backbone.Collection.extend({
model: Endeavor.Models.Member,
url: "Http://localhost:60000/api/members/" + "1", // $.cookie('UserId')
initialize: function () {
console.log("Members collections init");
}
});
Endeavor.Views.MemberView = Backbone.View.extend({
id: "memberForm",
template: "#memberTemplate",
initialize: function () {
console.log('init member view');
},
render: function () {
console.log('memberView render called');
console.log(this.model.toJSON());
var html = $(this.template).tmpl();
$(this.el).html(html);
$("#Name").text = this.model.Name;
}
});
jQuery(document).ready(function () {
// router
Endeavor.Router = Backbone.Router.extend({
routes: {
"": "lists",
},
lists: function () {
this.members = new Endeavor.Collections.Members();
this.members.fetch();
this.member = this.members.get(1);
var memberView = new Endeavor.Views.MemberView({ model: this.member });
memberView.render();
$("#content").html(memberView.el);
}
});
// populate local variables
var appRouter = new Endeavor.Router();
Backbone.history.start();
});
`.
Members.fetch is async call. Try to render view on members reset event, or pass success callback in fetch method, or call this.members.fetch({async: false}). There are many different options.

backbone.js Newbie Collection

I am trying to write some backbone.js stuff to get a better understanding on where and if it fits in better for me on projects. Any way I have a site and I am loading a collection with page content.
Json data comes back with (pid,name,title,content) on my router the default is
defaultRoute: function (actions)
{
this.showInfo('food');
},
showInfo: function (id)
{
var view = new ContentView({ model: this._items.at(id) });
$(".active").removeClass("active");
$("#" + id).addClass("active");
view.render();
}
if I put a 0 in place of id in this "new ContentView({ model: this._items.at(0) })" I will get the first item in the collection and if I do this in the View:
var ContentView = Backbone.View.extend({
el: $('#content'),
render: function ()
{
this.el.empty();
$(this.el).append(this.model.attributes.content);
return this;
}
});
I get the content displayed perfectly but of course may not be the content I wanted
Is it possible to select from a collection based on name == "food"?? I dont want to have to map the content to id numbers defeats the purpose of storing in a db
Sorry if this seems like a foolish question but I have crawled all over looking and Im sure Im missing something simple
here is my full NavigationRouter code in case it helps
var NavigationRouter = Backbone.Router.extend({
_data: null,
_items: null,
_view: null,
routes: {
"p/:id": "showInfo",
"*actions": "defaultRoute"
},
initialize: function (options)
{
var _this = this;
$.ajax({
url: "page_data.php",
dataType: 'json',
data: {},
async: false,
success: function (data)
{
_this._data = data;
_this._items = new ItemCollection(data);
_this._view.render();
Backbone.history.loadUrl();
}
});
return this;
},
defaultRoute: function (actions)
{
this.showInfo('home');
},
showInfo: function (id)
{
var view = new ContentView({ model: this._items.at(id) });
$(".active").removeClass("active");
$("#l_" + id).parent().addClass("active");
view.render();
}
});
Backbone mixes in a bunch of Underscore's functions into its Collections.
So if you want to find the model in the collection where name === 'food', you can do:
var foodModel = this._items.find(function(model) {
return model.get('name') === 'food';
});
// this will set foodModel to the first model whose name is 'food'
As a side note, you don't need to call empty in your render function, which can just be:
render: function() {
$(this.el).html(this.model.get('content'));
return this;
}
jQuery's html function just replaces the content of an element with the html string you pass in.

Resources