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;
}
Related
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
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.
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.
I have this backbone code, create a view and model, and calls the save method to save data to database:
var form = document.forms[0];
var RetailerModel = Backbone.Model.extend({
urlRoot: ' retailer.php',
defaults: {
name: 'company-name',
address: 'company-address',
phone: 'company-phone',
icon: 'http://localhost/icon.png'
}
});
var RetailerCollection = Backbone.Collection.extend({
});
var RetailerView = Backbone.View.extend({
className: 'retailer',
template: _.template($('#retailer-template').html()),
initialize: function() {
var obj = {
name: form.name.value,
address: form.address.value,
phone: form.phone.value
};
var o = this;
this.model.save(obj, {
success: function(model, response) {
console.log(model);
console.log(response);
o.render();
console.log('success');
},
error: function(model, response) {
console.log(model);
}
});
},
render: function() {
$('#retailer-list').append(this.$el.html(this.template(this.model.toJSON())));
return this;
}
});
var RetailerViews = Backbone.View.extend({
});
$('#submit').click(function(e){
var retailer_model = new RetailerModel();
var retailer_view = new RetailerView({model: retailer_model});
form.reset();
});
And the php code for receiving data is as follow:
<?php
$connect = mysql_connect('127.0.0.1','root','xxxxxx');
if (!$connect) {
die('Could not connect: ' . mysql_error());
}
mysql_select_db("retail", $connect);
if($_SERVER['REQUEST_METHOD'] == 'POST') //POST GET PUT DELETE
{
$data = json_decode(file_get_contents('php://input'), true);
}
$name = $data['name'];
$address = $data['address'];
$phone = $data['phone'];
$icon = $data['icon'];
if(!(mysql_query("INSERT INTO retailer (name, address, phone, icon)VALUES ('".$name."', '".$address."','$phone', '".$icon."')")))
{
echo 200;
}
else{
echo 'record has not been insert to db';
}
mysql_close($connect);
?>
One problem I'm having is that when the error function is called, the model returned from server still has modified attributes. I am wondering why this is happening and how can I make sure that if error happens, model stays unchanged.
Another question is in the php code, when the sql query is successful, if I echo 200, or '200', backbone will call success, but if I echo a string, backbone will call error, I'm wondering what's the logic behind it.
From the backbone docs:
Pass {wait: true} if you'd like to wait for the server before setting
the new attributes on the model.
If you don't want the model to update until after the save is success full pass wait: true as an option.
this.model.save(obj, {
success: function(model, response) {
console.log(model);
console.log(response);
o.render();
console.log('success');
},
error: function(model, response) {
console.log(model);
},
wait: true // Add this
});
The Backbone
save( so are others like fetch, update...)
returns a promise. You can use
save().done(
function( data ) {}, // success
function( err ) {} // fail
)
just like how you handle promises. The done() method is guaranteed to execute after the server has returned stuff.
See the jQuery API docs for AJAX.jqXHR for more information.
Backbone returns a promise.
Here is what I have to get it works.
save({wait: true}).success(function(data){
console.log(data); //returned data from server
}).error(function(error){
console.log(error); //error returned from server
});
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'
}
},