-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
error on save ( while trying to parse server response ) #2
Comments
I had the same Uncaught TypeError: Object # has no method 'parse' error. I fixed it by modifying both backbone-adapter and backbone, so it isn't a patch per se. Note: this is for the kanso packages:
Here's the changes and explanation of why. Here's the calling stack when the options.success error appears... Let's skip the jquery ajax calls (which send the server request and get the server response) and start in db onComplete after a successful response. /**
* Returns a function for handling ajax responses from jquery and calls
* the callback with the data or appropriate error.
*
* @param {Function} callback(err,response)
* @api private
*/
function onComplete(options, callback) {
...
if (req.status === 200 || req.status === 201 || req.status === 202) {
callback(null, resp);
}
... The callback is of the form {
"ok":true,
"id":"4d66bcc95c2e85abfc40efde7001658f",
"rev":"1-37c08db5877e8470f1b1dd831e5d37fe"
} backbone-adapter save exports.save = function (db, model, callback) {
db.saveDoc(model.attributes, function (err, resp) {
if (err) {
return callback(err);
}
model.attributes._id = resp.id;
model.attributes._rev = resp.rev;
callback(null, model.attributes);
});
}; Here we put in the _id and _rev attributes from the server response eg. "id":"4d66bcc95c2e85abfc40efde7001658f","rev":"1-37c08db5877e8470f1b1dd831e5d37fe" The callback(error, response) is populated with error = null & response = model.attributes. Why is response set to model.attributes and not the server response (resp)? Because backbone expects a JSON representation of the model like {
_id: "4d66bcc95c2e85abfc40efde7001658f",
_rev: "1-37c08db5877e8470f1b1dd831e5d37fe",
bar: "24",
createdAt: 1367955560200,
foo: "something",
name: "widget",
updatedAt: 1367955560200
} but the response from the server is {
"ok":true,
"id":"4d66bcc95c2e85abfc40efde7001658f",
"rev":"1-37c08db5877e8470f1b1dd831e5d37fe"
} Using the true server response alone would overwrite our model with ok, id, & rev attributes only. It would cause our model data to disappear. And disappearing data is not cool. backbone-adapter sync var callback = function (err) {
if (err) {
return options.error(model, err);
}
var args = Array.prototype.slice.call(arguments, 1);
options.success.apply(model, args);
}; This is where things start to go sideways... The line var args = Array.prototype.slice.call(arguments, 1); sets options.success.apply(model, args); calls options.success with a "single" array as the argument. Why? backbone.save // After a successful server-side save, the client is (optionally)
// updated with the server-side state.
if (options.parse === void 0) options.parse = true;
success = options.success;
options.success = function(model, resp, options) {
// Ensure attributes are restored during synchronous saves.
model.attributes = attributes;
var serverAttrs = model.parse(resp, options);
if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
return false;
}
if (success) success(model, resp, options);
}; The issue is that options.success is passed only one argument (model.attributes) when it is expecting (model, resp, options). Model isn't passed in all (see "where things go sideways" above). When model.parse is called, it cannot find it since it's calling model.attributes.parse, which doesn't exist. Hence the parse error. Fix var model = this; like so if (options.parse === void 0) options.parse = true;
var model = this;
success = options.success; Why can we do this? The model instance we are in extends backbone, so Without needing to pass in the model, we can change in backbone options.success = function(model, resp, options) { to options.success = function(resp, options) { and in backbone-adapter options.success.apply(model, args); to options.success.apply(args); options is already set in the model, so we don't need to pass this in either. Droping options in backbone... options.success = function(resp, options) { becomes options.success = function(resp) { In backbone 1.0.0 this change is also made. Ta da! So short version, if (options.parse === void 0) options.parse = true;
success = options.success;
options.success = function(model, resp, options) { to if (options.parse === void 0) options.parse = true;
var model = this;
success = options.success;
options.success = function(resp) { and change in backbone-adapter options.success.apply(model, args); to options.success.apply(args); I haven't tested this in all the scenarios yet (only save & sync) and it may require other modifications in backbone and backbone-adapter to cover the other scenarios. But it should fix the "Uncaught TypeError: Object # has no method 'parse'" error. |
I can not create a document and save it. It actually saves, but the "success" callback hits an error, and then I don't get my "success" ( or "error" ) result.
The problem is when it tries to call "model.parse" to get the server attributes after saving. The error is "model has no method 'parse'"
I have a very simple Kanso app which shows the problem.
This is the script on the page:
Again, the document does actually save, but it hits an exception just after the comment:
// Ensure attributes are restored during synchronous saves.
Uncaught TypeError: Object # has no method 'parse'
The line "model.attributes = attributes" does not seem correct.
At the point "attributes" points to the same object as "model", and this creates a self-reference.
The "model", at this point, also has the given attribute (name=Nick) directly on the object, not under an "attributes" sub-object. I thought backbone kept its "data" in "attributes".
...debugging more....
the line:
calls:
Which gets "attributes" instead of "model" in the first arg,...and "undefined" in the other 2 args. It then sets "model.attributes = attributes", which makes a complete mess of it...
Maybe the problem is the line
The callback itself only looks for (err), but it passes along its args to the next callback.
Maybe it should be something like:
( although "options" is not there at the time...but that is what the success callback later expects! )
The text was updated successfully, but these errors were encountered: