Backbone JS Collection not being populated - backbone.js

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

Related

Collection of unrelated Models in Backbone?

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>

Appending data to same collection after every pagination fetch

I am trying to populate instagram images using backbone,
I have basically 3 models as follows,
User model store all the user info related to instagram
App.Models.User = Backbone.Model.extend({
defaults: {
id: '',
access_token: '',
userid: '',
username: '',
full_name: '',
profile_picture: ''
},
urlRoot: "/api/user/",
initurl: function() {
return "https://api.instagram.com/v1/users/"+this.get('userid')+"/media/recent/?access_token=" + this.get('access_token');
},
initialize: function() {
this.set('id', $('#domdump .id').val());
this.fetch({
success: function(model) {
var photos = new App.Collections.Ig_photos([],{
url: model.initurl()
});
}
});
}
});
A model to store the next url for pagination
App.Models.Ig_next_url = Backbone.Model.extend({
defaults: {
next_url: ''
},
next_url:function(){
return this.get('next_url');
}
});
A model for the photo
App.Models.Ig_photo = Backbone.Model.extend({});
A collection for the multiple photo
App.Collections.Ig_photos = Backbone.Collection.extend({
model: App.Models.Ig_photo,
initialize: function(model, options) {
this.url = options.url;
this.nextSet();
},
sync: sync_jsonp,
parse: function( response ) {
if(response.pagination && response.pagination.next_url && response.pagination.next_url != this.url){
var next_url = new App.Models.Ig_next_url({ next_url: response.pagination.next_url });
this.url = next_url.next_url();
}
return response.data;
},
nextSet: function(){
this.fetch({
success: function(photos){
var ig_photos_views = new App.Views.Ig_photos_view({ collection: photos});
console.log(photos);
}
});
}
});
Also i have some views that does the render with a load more button that calls the nextset of the collection.
What i was trying to achieve is the photos get appended to the collection upon nextset() and the collection get updated with pervious data + new data but right now its getting overwritten.
Also is it okay to instantiate new collection from the modelfetch ?
You shouldn't need to make a new view. You should instead listen to the "add" event being triggered on the collection and render new items accordingly.
nextSet: function(){
this.fetch({add : true}); // The add option appends to the collection
}
This option is detailed in the very good documentation.

Backbone -- Can a model and a collection have their own separate urls?

I plan on doing separate fetches for an individual model and its collection, but would like the collection to follow the same structure of the model.
Currently, I have separate urls for each, but its crashing on a jQuery error Uncaught TypeError: Cannot read property 'length' of undefined
Should I be doing this a different way? Code is below:
ArticleModel.js
define([
'underscore',
'backbone',
], function(_, Backbone) {
var ArticleModel = Backbone.Model.extend({
defaults: {},
url : function() {
return baseAPIUrl + '/CoreService/GetArticle';
},
parse : function(data) {
console.log(data);
var articleArray = [];
$.each(data.Articles, function(i, item) {
if (isNotEmpty(item.ScaledImages)) {
var Image = item.ScaledImages.ImageUrls[4].Value;
}
articleArray = {
Id : item.Id,
Title : item.Title,
FeedTitle : item.FeedTitle,
Summary : item.Summary,
ImageUrl : Image,
Link: item.Link
};
});
return articleArray;
}
});
return ArticleModel;
});
ArticlesCollection.js
define([
'underscore',
'backbone',
'models/article/ArticleModel'
], function(_, Backbone, ArticleModel){
var ArticlesCollection = Backbone.Collection.extend({
model: ArticleModel,
initialize : function(articles, options) {
},
url : function() {
return baseAPIUrl + '/CoreService/GetFollowedMembersArticles';
},
parse : function(data) {
var articleArray = [];
$.each(data.Articles, function(i, item) {
if (item.ScaledImages != null) {
var image = item.ScaledImages.ImageUrls[4].Value;
}
articleArray.push({
Id : item.Id,
Title : item.Title,
FeedTitle : item.FeedTitle,
Summary : item.Summary,
ImageUrl : image,
Link: item.Link
});
});
return articleArray;
}
});
return ArticlesCollection;
});
Yes, for your model you would use:
var Model = Backbone.Model.extend({
idAttribute: "Id",
urlRoot: function() {
return baseAPIUrl + '/CoreService/GetArticle';
}
});
You would then instantiate your model like follows:
var model = new Model({Id: 2});
model.fetch();
Your api will call the following url then 'host/CoreService/GetArticle/2'
Yes. You can have different urls for models & collections. Only thing to worry would be the "url" and "urlRoot".
For a model it would be :
var User = Backbone.Model.extend({
urlRoot:'/saveuser.php'
});
For a collection it would be :
var Users = Backbone.Collection.extend({
url:'/saveuser.php'
});
Cheers

Backbone fetch doesn't work as expected

When I call fetch on my collection the app is calling the server and server returns an array of object. In the success function of the fetch call I've got an empty collection and the original response holding all objects that was responded by the server.
Collection
var OpenOrders = BaseCollection.extend({
model: Order,
url: baseUrl + '/api/orders?status=1'
});
Model
var Order = BaseModel.extend(
{
url:baseUrl + "/api/order",
defaults:{
order_items: new OrderList(),
location: 1,
remark: "remark"
},
initialize: function(options) {
var orderItems = this.get('order_items');
if (orderItems instanceof Array) {
orderItems = new OrderList(orderItems);
this.set({'order_items': orderItems})
}
orderItems.bind('change', _.bind(function() {
this.trigger('change')
}, this))
.bind('remove', _.bind(function() {
this.trigger('change')
}, this));
return this;
},
sum: function() {
return this.get('order_items').sum();
},
validate: function() {
return !!this.get('order_items').length;
},
add:function(product) {
this.get('order_items').add(product);
},
remove: function(product) {
this.get('order_items').remove(product);
}
);
Fetching the collection
this.collection.fetch({success:_.bind( function(collection, response){
console.log('OpenOrdersListView', collection.toJSON())
// logs []
console.log('OpenOrdersListView', response)
// logs [Object, Object ...]
}, this)})
Damm, its the validate method in my model. I've though validate have to return a boolean, but after reading the docs, it has to return an error message only if the model is not valid.
validate: function() {
if (!this.get('order_items').length){
return 'set minium of one product before save the order'
}
},

Backbone performing a GET immediately after a POST

I'm experimenting for the first time with backbone.js and I have a very simple Grails application with a single domain called Book. Things seem to be working well however, I've noticed that when I POST the data from the form to the server backbone then does a GET to the server with the ID of the new record. However, the POST returns the results as JSON and populates the table accordingly. I'm not sure I understand the need for the GET following the POST or how to stop this from happening.
$(function() {
// Model
window.Book = Backbone.Model.extend({
url: function() {
return this.id ? '/BackboneTest/books/' + this.id : '/BackboneTest/books.json';
},
defaults: { book: {
title: 'None entered',
description: 'None entered',
isbn: 'None entered'
}},
initialize: function() {
// can be used to initialize model attributes
}
});
// Collection
window.BookCollection = Backbone.Collection.extend({
model: Book,
url: '/BackboneTest/books.json'
});
window.Books = new BookCollection;
//View
window.BookView = Backbone.View.extend({
tagName: 'tr',
events: {
// can be used for handling events on the template
},
initialize: function() {
//this.render();
},
render: function() {
var book = this.model.toJSON();
//Template stuff
$(this.el).html(ich.book_template(book));
return this;
}
});
// Application View
window.AppView = Backbone.View.extend({
el: $('#book_app'),
events: {
"submit form":"createBook"
},
initialize: function() {
_.bindAll(this, 'addOne', 'addAll');
Books.bind('add', this.addOne);
Books.bind('refresh', this.addAll);
Books.bind('all', this.render);
Books.fetch();
},
addOne: function(book) {
var view = new BookView({model:book});
this.$('#book_table').append(view.render().el);
},
addAll: function() {
Books.each(this.addOne);
},
newAttributes: function(event) {
return { book: {
title: $('#title').val(),
description: $('#description').val(),
isbn: $('#isbn').val()
} }
},
createBook: function(e) {
e.preventDefault();
var params = this.newAttributes(e);
Books.create(params)
//TODO clear form fields
}
});
// Start the backbone app
window.App = new AppView;
});
I've determined that the cause of this was server side. Because of some scaffolded code that got generated for testing purposes, on the save, there was an additional redirect which resulted in a 302. This caused the GET after the POST. Once I cleaned up the server side code, I only get the POST, as expected.
Backbone usesPOST as a factory (getting the id from the server) with:
a payload request { title: 'None entered' }
a response { id: 12, title: 'None entered' }
It seems that your code trigger a GET action after the POST success. The code Books.bind('all', this.render) do not seems to be related to anything. It is not binded like add and there is no such method in the View.

Resources