get model by id from collection in backbonejs - backbone.js

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

Related

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.

parse not being called on my model in Backbone

I am working on developing a Backbone + require app. Things are working somewhat, but after updating a model on the server, though the server is returning a 200, the 'error' function in the options hash passed to the model's 'save' is being called.
I think I have identified the problem in that the server returns a JSON object containing 'id', whereas the model has an id attribute labeled 'aid'.
My understanding is that this should be handled in the model's 'parse' function, but I cannot get either the model's ''parse' function to be called. Here is my model:
define([
// These are path alias that we configured in our bootstrap
'jquery', // lib/jquery
'underscore', // lib/underscore
'backbone', // lib/backbone
'util'
], function($, _, Backbone){
// Above we have passed in jQuery, Underscore and Backbone
// They will not be accessible in the global scope
var Address = Backbone.Model.extend({
initialize: function() { console.log("Address initialized"); },
urlRoot: '/address/',
parse: function(response, options) {
console.log("In Address::parse");
for(thing in response) {
console.log("Key:" + thing + ", Val: " + response[thing]);
}
}
});
return {
address: Address
};
});
and here is the relevant part of my view:
events: {
"submit #add-address-form": "addAddress",
},
addAddress: function(ev) {
var that = this;
ev.preventDefault();
var addressDetails = $(ev.currentTarget).serializeObject();
var addr = new A.address();
addr.save(addressDetails, {
success: function(model, response, options) {
that.Backbone.application.router.navigate('', {trigger: true});
},
error: function(model, response, options) {
console.log("Response status: " + response.statusCode());
}
});
return false;
},
When the form presented by the view is submitted 'addAddress' is triggered and the server is updated. My app receives a 200 from the server, and the JSON object
'{id: }', but the parse function in the model is never called.
Any help appreciated;
You have to return a value in your parse function :
parse: function(response, options) {
console.log("In Address::parse");
for(thing in response) {
console.log("Key:" + thing + ", Val: " + response[thing]);
}
return response;
}

Backbone collection trigger twice when going back

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.

Backbone, collection models can't be accessed in function that fires from router Vent

So my issue is that the collection in my function that is being fired from my router vent/event aggregator does not have access to my main collection's fetched models.
My guess is that it's an asynchronous call issue, but how can I make it so the vented function call WAITS until the collection/models are fetched before executing? Or is that even my issue?
Here's is my relevant code. I'm using require.js and backbone to create a modular AMD app. Thank you so much in advance:
main.js
require(['views/app'], function (AppView) {
window.App = {
Vent : _.extend({}, Backbone.Events)
};
new AppView();
router.js
define([
'backbone',
], function(Backbone){
var MainRouter = Backbone.Router.extend({
routes: {
'levelone/:id':'showWork'
},
showWork: function (index){
App.Vent.trigger('addressChange', {
index: index
});
}
});
return MainRouter;
});
App.js
define([
'backbone',
'views/levelone/LevelOneView',
'views/leveltwo/LevelTwoView',
'views/static/StaticView',
'router'
],
function(Backbone, LevelOneView, LevelTwoView, StaticView, MainRouter){
var AppView = Backbone.View.extend({
el: $("body"),
events: {
...
},
initialize: function(){
new LevelOneView();
App.router = new MainRouter();
Backbone.history.start();
},
.............
LevelOneView.js
initialize:function() {
this.getCollection();
this.domSetup();
App.Vent.on('addressChange', this.addressChange, this);
},
getCollection : function(){
var self = this;
onDataHandler = function(collection) {
self.LevelTwoCollectionGrab();
};
this.collection = new LevelOneCollection([]);
this.collection.fetch({ success : onDataHandler, dataType: "jsonp" });
},
// We grab a Level Two Collection here so we can take the ids from it and add them to our Level One collection.
// This is necessary so we can create links between the two levels.
LevelTwoCollectionGrab: function(){
var self = this;
this.leveltwocollection = new LevelTwoCollectionBase([]);
onDataHandler = function(collection){
self.render();
self.$el.animate({
'opacity': 1
}, 1200);
self.renderLevelTwoIds();
self.setLevelTwoids();
self.attachLevelTwoLink();
}
this.leveltwocollection.fetch({success : onDataHandler, dataType: "jsonp"});
},
renderLevelTwoIds: function(){
return this;
},
render: function(){
var pathname = window.location.hash;
this.setModelId(this.collection.models);
this.addPositionsToIndex();
this.determineModels();
this.attachLevelTwoLink();
.......
},
addressChange: function(opts){
console.log(this.collection.models)
//returns a big fat empty array. WHY?!
}
You could use the jQuery Promises returned by fetch to help you know when both collections are fetched.
initialize:function() {
this.getCollection();
this.domSetup();
App.Vent.on('addressChange', this.addressChange, this);
},
getCollection : function(){
var self = this;
console.log('should be first');
this.collection = new LevelOneCollection([]);
this.fetchingLevelOne = this.collection.fetch({ dataType: "jsonp" });
this.fetchingLevelTwo = this.leveltwocollection.fetch({ dataType: "jsonp"});
// wait for both collections to be done fetching.
// this one will always be called before the one in addressChange
$.when(this.fetchingCollectionOne, this.fetchingCollectionTwo).done(function(){
console.log('should be second');
self.render();
self.$el.animate({
'opacity': 1
}, 1200);
self.renderLevelTwoIds();
self.setLevelTwoids();
self.attachLevelTwoLink();
});
},
renderLevelTwoIds: function(){
return this;
},
render: function(){
var pathname = window.location.hash;
this.setModelId(this.collection.models);
this.addPositionsToIndex();
this.determineModels();
this.attachLevelTwoLink();
.......
},
addressChange: function(opts){
var self = this;
// wait for both collections to be done fetching.
// this one will always be called AFTER the one in getCollection
$.when(this.fetchingCollectionOne, this.fetchingCollectionTwo).done(function(){
console.log('should be third');
console.log(self.collection.models);
});
}
A nice thing about this, if the user is very very fast at typing in the address bar, and several addressChange calls are made, they will all wait until the collections are fetched and will execute in the proper order.
I think I solved it. Basically, I'm now calling the function inside of $.when function--
Like so:
$.when(this.collection.fetch(), this.leveltwocollection.fetch()).done(function(){
$.when(self.render()).done(function(){
_.each(self.collection.models, function(model){
var wpid = model.get('id'),
bbid = model.id;
if (wpid == index){
window.App.InfoPos.pos5 = bbid;
var modelinfo = model.toJSON();
$('.box5').empty();
$('.box5').html(tmplOne(modelinfo));
self.$el.animate({
'opacity': 1
}, 1200);
}
});
});
});
The function launches from inside the when call and then waits until completed before executing anything in the done function. Works now! Thanks for the help all, especially you Paul.

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