Backbone collection trigger twice when going back - backbone.js

I have a weard issue with a collection, when I first load my compositeView everything is working great but then when I start navigate in my app and then comeback to my compositeView(Backbone.history.navigate) it looks like my collection is called twice (my itemviews are fired twice).
I have try to debug, but I fetch my collection only once, the is only one init, the router seems to be ok too.
Here is my compositeView:
'use strict';
define(["jquery", "backbone", "marionette", "text!templates/portraits/portrait.html", "view/portraits/portraitItemView", "collection/portraitCollection", "application", "JSMovieclip"], function($, Backbone, Marionette, template, PortraitItemView, portraitCollection, App) {
var PortraitsCompositeView = Marionette.CompositeView.extend({
template : _.template(template),
collection : portraitCollection,
tagName: "div",
id : "articles",
itemView : PortraitItemView,
itemViewContainer : '#list-articles',
itemViewOptions: {
collection: portraitCollection
},
initialize : function (options) {
_.bindAll(this);
this.options = options || {};
this.collection.fetch({
type: 'POST',
success : function(data, raw) {
App.execute('loader', false);
}
});
},
And here is my collection :
'use strict';
define(["jquery", "underscore", "backbone", "marionette", "model/portraitsModel"], function($, _, Backbone, Marionette, PortraitModel) {
var PortraitCollection = Backbone.Collection.extend({
model : PortraitModel,
sync: function(method, model, options) {
var params = _.extend({
type: 'GET',
dataType: 'jsonp',
url: 'http://backend.url.fr/api/portraits/get_list/',
processData: false
}, options);
return $.ajax(params);
},
parse : function(response) {
this.totalLength = response.count;
return response.portraits;
}
});
return new PortraitCollection;
});

Your collection fetch is appending the items to itself.
You can add reset:true to your Collection.fetch properties
initialize : function (options) {
_.bindAll(this);
this.options = options || {};
this.collection.fetch({
reset: true,
type: 'POST',
success : function(data, raw) {
App.execute('loader', false);
}
});
},

I finally found my error, my json was returning an empty "id" field, after fixing it, everything works great.

Related

Uncaught TypeError: Cannot read property 'navigate' of undefined

So I developed a simple CRUD program from the tutorial video of backbonejs.org and the code worked fine. Now I'm trying to implement the code in requirejs but it shows following error in the following code: -
define([
'jquery',
'underscore',
'backbone',
'router',
'models/Customers/Customer',
'helper/Serialize'
], function ($, _, Backbone, Router, Customer, Serialize) {
var CustomerEditView = Backbone.View.extend({
el: '.page',
events: {
'submit .edit-customer-form': 'saveCustomer',
'click .delete': 'deleteCustomer',
},
saveCustomer: function (ev) {
var customerDetails = $(ev.currentTarget).serializeObject();
var customer = new Customer();
customer.save(customerDetails, {
success: function (customer) {
this.router.navigate('', { trigger: true });
}
});
return false;
},
You can use :
customer.save(customerDetails, {
success: function (customer) {
Backbone.history.navigate('', { trigger: true });
}
if you want to use router object first you have to initialize it like
this.router = new router();
and you can say this.router.navigate('', { trigger: true });
it is not optimal to create a new instance in all the views and not suggested to make the object global. You can use Backbone.history.nvaigate which is alias to router.nvaigate

get model by id from collection in backbonejs

I have following kind of collection
[
{
"id": "2324324",
"name": "name",
"type": "type",
},
{
"id": "59980",
"name": "name",
"type": "type",
}
]
model:
define(['underscore', 'backbone'], function(_, Backbone){
//Define Alert model with default properties and value
var abcModel = Backbone.Model.extend({
idAttribute:"_id",
defaults:{
// My properties
},
initialize:function(){
}
});
return abcModel;
});
collection
define(['underscore', 'backbone', 'models/abcModel', 'app/utils'], function(_, Backbone, abcModel, Utils) {
var self;
//List of Alerts stored in Backbone Collection
abcListCollection = Backbone.Collection.extend({
model: abcModel ,
initialize: function() {
self = this;
this.model=abcModel ;
},
fetchData: function(obj) {
add=true;
var data = {
"method": "method name",
"params": {
param1:"param1",
param2:"param2"
}
}
Utils.Ajax.post(Utils.WebAPI.WebAPIServer, data, function(response, textStatus, jqXHR) {
obj.success.call(self.collection, response);
}, 'json', function(err) {
console.log(JSON.stringify(err));
obj.error.call(err);
}, "loading");
},
collection: {}
});
return abcListCollection;
});
view
define(['jquery', 'underscore', 'backbone', 'text!views/abcView/abcListView.html','views/abcView/ListTemplate' ,'app/utils', 'collection/abcListCollection'], function($, _, Backbone, tmpl_abcummaryView, abcListView, Utils, abcListCollection) {
var abcListView = Backbone.View.extend({
// Setting the view's template property using the Underscore template method
template: _.template(tmpl_abcummaryView),
// View constructor
initialize: function() {
abcCollection= new abcListCollection();
mainRouter.collections.abc= new abcListCollection();
},
// View Event Handlers
events: {
},
// Renders the view's template to the UI
render: function() {
var self=this;
this.$el.html(this.template({data: this.templateData}));
abcCollection.fetchData({
success: function (collection, response) {
_.each(collection, function (obj) {
mainRouter.collections.abc.add(obj);
})
},
error: function (err) {
console.log("error");
}
});
var model1=mainRouter.collections.abc.get(2324324);
// Maintains chainability
return this;
}
});
return abcListView;
});
var model1=mainRouter.collections.abc.get(2324324);
But it is returning undefined.
You could try
mainRouter.collections.abc.findWhere( { id : 2324324 });
However, it seems that your timing could also be out.
the .fetchData function would be an asynchronous call, meaning that the success function would actually execute after the line
var model1 = mainRouter.collectins.abc.get(2324324);
Put a debug breakpoint on the above line, and also the success function - and see which one executes first.
Your fetchData is a asynchronous function. It would be executed in the event loop after that async call is resolved. Your code is not blocking at that call. It just goes over that and executes the render function completely. After some time, when that call would return and your success callback would be called, you get something in your collection.
Putting the code of getting the model from the collection is right and should be put in a callback after you have added models to the collection.
see Collection get http://backbonejs.org/#Collection-get
so one way to do is to write:
success: function (collection, response) {
_.each(collection, function (obj) {
mainRouter.collections.abc.add(obj);
})
var model1 = mainRouter.collectins.abc.get(2324324);
},
However it does not seem right to use your model in your view. but that is the design issue that you have to think about.
Also, i think that you should read a little more about Javascript event driven architecture. I have written a simple blog : Learning Javascript

How can I set a dynamic className on a model(parse object) using parse.com with backbonejs

Basically I have a model/collection that I want to reuse. I have a couple different collections on parse.com 'https://api.parse.com/1/classes/foo' and 'https://api.parse.com/1/classes/bar'.
My Collection:
define([
"app",
"models/listModel",
], function (app, ListModel) {
var ListCollection = Parse.Collection.extend({
model: ListModel,
});
return ListCollection;
});
My Model:
define([
"app"
],
function(app) {
var ListModel = Parse.Object.extend({
className: null,
initialize: function(attrs, options) {
this.className = app.foobar ? 'foo' : 'bar';
}
});
return ListModel;
});
And then I create a new collection:
app.foobar = 'bar';
var collection = new ListCollection();
collection.fetch({
wait: true,
success: function(collection, response, error) {
console.log(collection);
}
});
And this is the error I get:
Uncaught Error: Parse.Object.extend's first argument should be the className.
That gets thrown before initialize is ever fired in the model.

uncaught typeerror: object function has no method 'tojson'

I have created a model like this
define(['backbone', 'text_dictionary'], function(Backbone, Text_Dict) {
var IndexPageModel = Backbone.Model.extend({
defaults:{
val_btn_gotohomepage : Text_Dict.val_btn_gotohomepage,
val_btn_gotologinpage : Text_Dict.val_btn_gotologinpage,
val_btn_gotolistpage : Text_Dict.val_btn_gotolistpage
}
});
return IndexPageModel;
});
and instantiated this model with 'new' in my page code like this
define([ 'page_layout',
'panel_itemview',
'header_itemview',
'content_itemview',
'footer_itemview',
'templates',
'text_dictionary',
'indexpage_model',
'indexpage_logic'],
function( Page,
Panel,
Header,
Content,
Footer,
Templates,
Text_Dict,
IndexPageModel,
IndexPage_BusnLogic) {
console.log("Success..Inside Index Page.");
var Page_Index = {};
Page_Index.page = (function(){
var _pageName = Text_Dict.indexpage_name;
var _pageModel = new IndexPageModel();
return _pageLayout = Page.pageLayout({
name:_pageName,
panelView: Panel.panelView({name:_pageName, pagetemplate: Templates.simple_panel}),
headerView: Header.headerView({name:_pageName, title: Text_Dict.indexpage_header, pagetemplate: Templates.header_with_buttons}),
contentView: Content.contentView({name:_pageName, page_model:_pageModel, pagetemplate:Templates.content_index, busn_logic:IndexPage_BusnLogic.HandleEvents}),
footerView: Footer.footerView({name:_pageName, title: Text_Dict.indexpage_footer, pagetemplate: Templates.simple_footer})
});
})();
return Page_Index;
});
my page gets created using the page layout
define([ 'underscore', 'marionette' ], function( _, Marionette ) {
console.log("Success..Inside Index View.");
var Page = {};
var _ReplaceWithRegion = Marionette.Region.extend({
open: function(view){
//Need this to keep Panel/Header/Content/Footer at the same level for panel to work properly
this.$el.replaceWith(view.el);
}
});
Page.pageLayout = function (opts) {
var _opts = _.extend ({ name: 'noname',
panelView: null,
headerView: null,
contentView: null,
footerView: null,
}, opts);
return new ( Marionette.Layout.extend({
tagName: 'section',
attributes: function() {
return {
'id': 'page_' + _opts.name,
'data-url': 'page_' + _opts.name,
'data-role': 'page',
'data-theme': 'a'
};
},
template: function () {
return "<div region_id='panel'/><div region_id='header'/><div region_id='content'/><div region_id='footer'/>";
},
regions: {
panel: {selector: "[region_id=panel]", regionType: _ReplaceWithRegion},
header: {selector: "[region_id=header]", regionType: _ReplaceWithRegion},
content: {selector: "[region_id=content]", regionType: _ReplaceWithRegion},
footer: {selector: "[region_id=footer]", regionType: _ReplaceWithRegion},
},
initialize: function(){
$('body').append(this.$el);
this.render();
},
onRender: function() {
if (this.options.panelView) {
this.panel.show (this.options.panelView);
};
if (this.options.headerView) {
this.header.show (this.options.headerView);
};
if (this.options.contentView) {
this.content.show(this.options.contentView);
};
if (this.options.footerView) {
this.footer.show (this.options.footerView);
};
},
}))(_opts);
};
return Page;
});
but in my itemview when i am passing model reference like this
define([ 'underscore', 'marionette', 'event_dictionary', 'app' ], function(_,
Marionette, Event_Dict, App) {
console.log("Success..Inside Content Index View.");
var Content = {};
Content.contentView = function(opts) {
return new (Marionette.ItemView.extend({
tagName : 'div',
attributes : function() {
console.log('options name==' + opts.name);
console.log("page model=="+opts.page_model);
return {
'region_id' : 'content',
'id' : 'content_' + opts.name,
'data-role' : 'content'
};
},
initialize : function() {
_.bindAll(this, "template");
},
template : function() {
return opts.pagetemplate;
},
model : function() {
return opts.page_model;
}
}))(opts);
};
return Content;
});
It's giving me error
Uncaught TypeError: Object function () {
return opts.page_model;
} has no method 'toJSON'
The model property of a view cannot be a function. Backbone allows this for some things like url (by way of the _.result helper function), but not in this case. Change your view code to not have a model function and just do this in initialize:
initialize: function (options) {
this.model = this.page_model = options.page_model;
}
UPDATE since you won't just take my word for it, here is the Marionette source that is almost certainly the top of your exception stack trace. Once again: view.model has to be a model object not a function. Fix that and the error will go away.
The accepted answer is correct, but it took a bit of messing about to find out why I had that error coming up, so I'm offering what the solution for my personal use-case was in case it helps anyone else stumbling upon this page in the future.
I had this:
app.module 'Widget.Meta', (Meta, app, Backbone, Marionette, $, _) ->
Meta.metaView = Backbone.Marionette.ItemView.extend
model: app.Entities.Models.meta
template: '#meta-template'
... when I should have had this:
app.module 'Widget.Meta', (Meta, app, Backbone, Marionette, $, _) ->
Meta.metaView = Backbone.Marionette.ItemView.extend
model: new app.Entities.Models.meta()
template: '#meta-template'
It's just a matter of instantiating the function definition.

Handeling response from backbone.js collection using fetch

am pretty new to backbone.js and managed recently to finish my first application. I made a collection that is responsible for fetching data through a API but am not able to loop through the result and use it.
Here is my model file
define([
'jquery',
'underscore',
'backbone'
], function($, _, Backbone){
var VehicleLookupModel = Backbone.Model.extend({
//data will contain one of the items returned from the collection's 'parse' function.
parse: function(data){
return data;
}
})
return VehicleLookupModel;
});
collection file
define([
'jquery',
'underscore',
'backbone',
'l/models/VehicleLookupModel'
], function($, _, Backbone, VehicleLookupModel){
var VehicleLookupModelSet = Backbone.Collection.extend({
model : VehicleLookupModel,
url : function() {
return '/en/car/api/model-lookup-model.json/'+this.make+'/';
},
parse : function(response) {
return response;
},
initialize: function(options) {
options || (options = {});
this.make = options.make;
}
})
return VehicleLookupModelSet;
});
and finally the view file
define([
'jquery',
'underscore',
'backbone',
'l/collections/VehicleLookupMakeSet',
'l/collections/VehicleLookupModelSet',
'l/collections/VehicleLookupTrimSet'
], function($, _, Backbone, VehicleLookupMakeSet, VehicleLookupModelSet, VehicleLookupTrimSet){
var BrowseVehicleView = Backbone.View.extend({
el: $('#vehicle-browse-form'),
initialize: function(){
// Extend JQuery example
// This would extend JQuery function for resetting elements on the form
$.fn.extend({
resetElement: function(){
$(this).attr('disabled', 'disabled');
$(this).html('');
return $(this);
}
});
// define array of elements to be used in DOM manipulations
this.elements = {
"make" : $('#id_make', this.el),
"model" : $('#id_model', this.el),
"trim" : $('#id_trim', this.el),
"year" : $('#id_year', this.el)
}
},
events: {
"change #id_make" : "onMakeChange",
"change #id_model" : "onModelChange",
"change #id_trim" : "onTrimChange"
},
render: function(){
// Using Underscore we can compile our template with data
},
onMakeChange: function(event) {
this.elements.model.resetElement();
this.elements.trim.resetElement();
this.collection = new VehicleLookupModelSet({make: this.elements.make.val()})
this.collection.fetch();
console.log(this.collection);
},
onModelChange: function(event) {
var VehicleLookupTrimInstance = new VehicleLookupTrimSet({make: this.elements.make.val(), model: this.elements.model.val()})
VehicleLookupTrimInstance.fetch();
},
onTrimChange: function(event) {
},
renderItem: function(object, item) {
console.log(item);
}
});
// Our module now returns our view
return new BrowseVehicleView;
});
The above is console.log(this.collection) is returning an object with many property which am not sure how to use. But, I noticed that there is a method "models" and inside models there is many number of objects, each represent the value of the json.
Any ideas how i can loop through the object?
this.collection.fetch({
success: function(collection, response) {
_.each(collection.models, function(model) {
console.log(model.toJSON());
})
}
});

Resources