parseJSON(response) is null - backbone.js

I have a Backbone model that is making a successful server request. The callback is using backbone's trigger method to trigger an event in the associated view, and its passing the parsed json response from the server request as the second parameter of the trigger method, as described in the backbone documents. In the view, the event triggers the render method, but the response object is null in the view. It's throwing this error
Uncaught TypeError: Cannot read property 'word' of null
Can anyone see what I might be doing wrong?
The server request from the model
new: function() {
var _this = this;
console.log("new function of model");
$.ajax({
url: "/gamestart",
type: "GET",
success: function(response) {
console.log(response);
var json = $.parseJSON(response);
_this.set({lost: false});
_this.set({win: false});
_this.trigger("gameStartedEvent", json);
}
})
},
the event in the initializer method of the view which triggers the render method to render the response
this.model.on("gameStartedEvent", this.render, this);
the render method where the response is null
render: function(response) {
$("#hint").show();
console.log(response);
var html = this.template({characters: response.word});
this.el.hide();
this.el.html(html).show();
},
Note, if it matters, the view is being instantiated with the model
var word_view = new WordView({model: game})
Update
Actually the error's happening here. The response is returning successfully but i'm parsing it incorrectly. The variable json is null when I check the console. Can you see what I'm doing wrong?
var json = $.parseJSON(response);
console.log(json)
Response
Object {word: Array[6]}
word: Array[6]
__proto__: Object

There was actually no need to call parseJSON on the response object. I could get the word property directly off the response object, so I just passed the response object as the second argument to trigger, and called response.word in the view.
$.ajax({
url: "/gamestart",
type: "GET",
success: function(response) {
console.log(response.word);
var json = $.parseJSON(response); ####unnecessary to parseJSON here
console.log(json);
_this.set({lost: false});
_this.set({win: false});
_this.trigger("gameStartedEvent", response);
}
})
},

Related

How to convert Angular $resource object to regular object without methods

I am starting to learn AngularJS $resource, and noticed the $resource object has a few methods (see below for examples) attached to my data downloaded from server. How do I remove these methods and convert the object to a regular (array) object?
__proto__: Resource $delete: function (params, success, error) {$get: function (params, success, error) {$query: function (params, success, error) {$remove: function (params, success, error) {$save: function (params, success, error) {constructor: function Resource(value) {toJSON: function () {__proto__: Object
For instance, I'm trying to send a POST request including some key value data using $resource.save, but these 'proto' items in the array is somehow causing the data to become "undefined" when passed to $.param(data) in the factory. I could do the same thing using $http with ease, but want to learn $resource. Thanks!
Inside a Controller
$scope.ok = function () {
$scope.entry = new calEntry();
$scope.entry.data = data // data is $resource object including _proto_ items
$scope.entry.$save( function(){
toaster.pop('success','Message','Update successfully completed.');
});
};
Factory
myApp.factory("calEntry",['$resource','$filter', function($resource, $filter) {
return $resource("/griddata/", {}, {
save: {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
transformRequest: function(data, headersGetter) {
return $.param(data); // data is undefined when it reaches here
}
}
});
}]);
Try the toJSON function, it will fetch the data and remove the extra properties.

Rectangular addResponseInterceptor data object undefined while has it in response object

Using Restangular, the get method/promise resolves but the result handed to .then() is empty...with console.log(data); showing undefined. I checked the network tab in chromium debug and the xhr request is good with 200 success...there is a full json response in the body.
Using addResponseInterceptor, I have found that the data argument is undefined, but the response argument shows the object containing the data property with the payload/body.
So, I am left scratching my head.....why is the data argument undefined while the response object properly contains the json payload/response in the response.data?
I need to resolve this so the result is passed to .then() on resolve.
createNode: function(node) {
var account = "9936";
var eventId = "0fd6afd9-4aa0-a5c9-ff0b3e60cdcf";
Restangular.addResponseInterceptor(function(data, operation, what, url, response, deferred) {
console.log("````````````````");
console.log(data);
console.log(operation);
console.log(what);
console.log(url);
console.log(response);
console.log(deferred);
console.log("````````````````");
});
node.js:12
undefined node.js:13
get node.js:14
event node.js:15
/1.0/loadbalancers/account/9936/event/0fd6afd9-4aa0-a5c9-ff0b3e60cdcf node.js:16
^Object {data: Object, status: 200, headers: function, config: Object}
config: Object
data: Object
Automation: Object
Classification: Array[2]
ExecutionTimestamp: "2014-08-13T16:08:37.4676Z"
ID: "0fd6afd9-a5c9-ff0b3e60cdcf"
Incident: Object
LastStatus: "ENQUEUED"
Metadata: Object
Name: "Node Create"
RecordLogs: false
Source: "Device Lifecycle"
Stream: Object
Targets: Array[1]
Make sure all your response interceptors return the data at the end:
Restangular.addResponseInterceptor(function(data, operation, what, url, response, deferred) {
// your code here
return data;
});
Not returning anything will set the data to undefined on following response interceptors calls.

Example of progress on file upload in AngularJS $http POST

I am trying to get a 'progress' event from AngularJS $http POST request for file upload.
After looking at $http upload file progress in AngularJS, I came across one recent angular.js git commit, that suppose to resolve the issue Add XHR progress event handling to $http and $httpBackend.
Did anyone achieve this working? And if so, can kindly show the example?
PS. I'd prefer to stay with $http rather than create my own XMLHttpRequest. The reason is that my backend expects to get json object combined with multipart file data. And the attempt to make through XMLHttpRequest is failing with error message that backend doesn't see the json object part of request "Required String parameter 'objData' is not present. The request sent by the client was syntactically incorrect." While in the POST message I see "Content-Disposition: form-data; name="objData"" in Firebug.
$scope.uploadFile = function() {
var url = buildUrl('/upload');
var data = {objData: $scope.data, fileData: $scope.file};
var formData = new FormData();
formData.append("objData", angular.toJson(data.objData));
formData.append("fileData", data.fileData);
var xhr = new window.XMLHttpRequest();
xhr.upload.addEventListener("progress", uploadProgress, false);
xhr.open("POST", url);
xhr.setRequestHeader("Content-Type","application/json;charset=utf-8");
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.send(formData);
};
At time of writing $http doesn't support the notify method of the new 1.2 $q. So you have to use jquery xhr. Its rather simple once set up:
Notice that we return a promise so your consumer of uploadFile would do uploadFile(..).then(success, fail, progress)
$scope.uploadFile = function() {
var deferred = $q.defer();
var getProgressListener = function(deferred) {
return function(event) {
//do some magic
deferred.notify(magic);
};
};
var formData = new FormData();
formData.append("objData", angular.toJson(data.objData));
formData.append("fileData", data.fileData);
$.ajax({
type: 'POST',
url: buildUrl('/upload'),
data: formData,
cache: false,
// Force this to be read from FormData
contentType: false,
processData: false,
success: function(response, textStatus, jqXHR) {
deferred.resolve(response);
},
error: function(jqXHR, textStatus, errorThrown) {
deferred.reject(errorThrown);
},
xhr: function() {
var myXhr = $.ajaxSettings.xhr();
if (myXhr.upload) {
myXhr.upload.addEventListener(
'progress', getProgressListener(deferred), false);
} else {
$log.log('Upload progress is not supported.');
}
return myXhr;
}
});
return deferred.promise;
};

How to get a good response from saving a model

With backbone.js I'm saving a model. A PUT is send to the server and the response is returned. The first time I do it, it returns success, the following times an error is return, because after the first time the response is added to the model.
Save function in Backbone.js:
saveCountry: function() {
this.model.save({},{
success: function(model, response) {
console.log('success! ' + response);
},
error: function(model, response) {
console.log('error! ' + response);
}
});
this.render();
return false;
},
PHP returns a JSON-string:
{"response": "Model saved!"}
Following PUT's get an error as response, because 'response' is added to the model:
Unknown column 'response' in 'field list'
Why is the response added to the model and how do I prevent it?
From Backbone's documentation on model save:
Set a hash of model attributes, and sync the model to the server. If
the server returns an attributes hash that differs, the model's state
will be set again.
http://documentcloud.github.com/backbone/docs/backbone.html#section-41
What to do to make it work: don't return {"response": "Model saved!"} from the server. Just return a success (status 200) with no content.
If the save did not work, return a JSON with the errors, and Backbone will trigger an error event on your model, with the JSON you provided (see http://documentcloud.github.com/backbone/docs/backbone.html#section-41 and http://documentcloud.github.com/backbone/docs/backbone.html#section-145).
Just to resurrect an ancient thread...
It isn't always possible/desirable to change the response you get back from the server.
Another solution is to override parse in the model to take care of this. For your situation, where the response is inappropriate for ALL of your models, you could do it in a superclass.
MyModel = Backbone.Model.extend({
parse: function(data) {
delete data["success"];
return data;
}
});
Address = MyModel.extend({
saveCountry: function() {
this.model.save({},{
success: function(model, response) {
console.log('success! ' + response);
},
error: function(model, response) {
console.log('error! ' + response);
}
});
this.render();
return false;
},
...
});

How do I trigger the success callback on a model.save()?

this.model.save({
success: function(model, response){
console.log('success');
},
error: function(){
console.log('error');
}
})
The model is correctly posted to the server which handles the save, but the success callback is not fired. Do I need to send something back from the server ?
The first argument of save is the attributes to save on the model:
this.model.save( {att1 : "value"}, {success :handler1, error: handler2});
For some unknown reason, none of the above method worked for me. The api only was not hit in my case.
But later while searching on this, I bumped into this link, where some one had tried null instead of {} as the first parameter.
this.model.save(null, {
success: function (model, response) {
console.log("success");
},
error: function (model, response) {
console.log("error");
}
});
so, this worked for me. Hope this helps you too.
Your server must return a JSON object. If the response is not a JSON object, the callbacks will not fire.
If for success your server doesn't return a JSON object, perform a save with dataType:"text" option, like this:
this.model.save([],{
dataType:"text",
success:function() {},
error:function() {}
});
With this option it will not be waiting for a JSON in response, but a text, and thus the callback will be launched.
You may use underscore lib as follows as backbone already depends upon this. Remember first argument of save must either have attributes or you may just pass {} in case you want to save model itself.
this.model.save({}, _.bind(function(model, response){
//Do whatever you want e.g.
this.collection.add(model)
}, this))
so im a little confused - do i still need to pass in all attributes in order for me to call a save event? what if my model is large.. i dont wish to set every property manually
im calling model.save and attempting to do the following:
this.model.save(
{
success: function (model, response) {
console.log('model saved');
}
});
ok just to answer my own question incase anyone finds this post, i did the following which works:
this.model.save({ id: this.model.get('id') },
{
success: function (model, response) {
console.log("success");
},
error: function (model, response) {
console.log("error");
}
});
EDIT: I couldn't reply to you for some reason, but I can edit
but you don't have to set id: this.model.get('id') you can just pass a blank object because a blank attribute just won't extend attributes, does nothing:
this.model.save({}, {
success: function (model, response) {
console.log("success");
},
error: function (model, response) {
console.log("error");
}
});
The following is the code that i am using for backbone model save.
this.model.save(model,{
success:function(model){
console.log("Saved Successfully");
},
error:function(model){
console.log("Error");
}
});
Cheers
Roy M J
For those that want to save a model, without updating the attributes, you can do the following:
model.once("sync", function(model, response, options){
//
});
model.once("error", function(model, response, options){
//
});
model.save();
In you initialize function, bind the sync method to a method you define (onSaveSuccess)
initialize: function (options) {
this.model.on('sync', _.bind(this.onSaveSuccess, this));
},
onSaveSuccess: function() {
console.log('saved');
this.render();
},
This way, any time you run this.model.save(), it will run the onSaveSuccess function as a callback if your sync is successful

Resources