First of all my knowledge of Backbone is very limited, and currently, I'm trying to add an implementation into some code I didn't create.
My problem is as follows:
I have a collection which is being rendered on click of a button. Now, we are setting some permissions on the website, so that sometimes the response I will get is a 401.
I'm currently able to get the response, the problem is that I don't know how to attach it to the sync event so that if I get a 401 when I call the API, it shouldn't render anything.
I would think looking at the code would help clarify my problem:
this.addressBook = new (Backbone.Collection.extend({
url: url,
model: Backbone.Model.extend({
idAttribute: 'ID'
}),
parse: function(data) {
return data;
}
}))();
this.addressBook.on('sync', this.renderAddresses, this);
this.addressBook.fetch();
So I found a few ways to get the status code from fetch, but in this particular case, I need to get the status code before the sync event calls this.renderAddress, and given the status of the response, go ahead and render my view or simply display a message stating that access is denied.
Sorry if I'm not clear enough.
Thanks in advance.
Here is the salient part of the backbone documentation:
Whenever a model or collection begins a sync with the server, a "request" event is emitted. If the request completes successfully you'll get a "sync" event, and an "error" event if not.
This means that the sync event shouldn't fire if you get a 401, rather the error event should be triggered.
You can test this in your code by listening to all the different events (all available parameters included):
this.listenTo(yourCollection, 'request', function(collection, resp, options) {
console.log('Request: ', resp);
});
this.listenTo(yourCollection, 'error', function(collection, resp, options) {
console.log('Error: ', resp);
});
this.listenTo(yourCollection, 'sync', function(collection, resp, options) {
console.log('Sync: ', resp);
});
So you should be able to just listen to the error event to display your custom error message:
this.listenTo(yourCollection, 'error', function(collection, resp) {
if (resp.status === 401) {
console.warn('401: Unauthorized');
} else {
// do something else
}
});
First of all, you will need to define some callback options for you addressBook fetch. For what you are trying to accomplish, you need to provide the error callback.
this.addressBook.fetch({
error: function(xhr) {
if(xhr.status == 401) { // Put logic you want in the case of 401 here...
}
});
Disclaimer: I have not tested this code, as I do not have a convenient way I can think of to reproduce this problem.
I believe it works because Backbone passes a jQuery xhr Object to its error callback. http://backbonejs.org/#Model-fetch
Related
We're building a node.js application using Express, but are separating our layers. So the browser is running pure jQuery and javascript, the web server and application server are Node.js and Express. We're using REST APIs between them all.
We're using jQuery 1.10.2
Since the application server cannot be open to the public, the browser must make API calls to the web server, which manages making the call to the application server and returning the results. Here is what that looks like...
// Proxy all other API calls to the backend server
var request = require('request');
app.all('/api/*', function (req, res) {
var targeturl = apihost+req.originalUrl;
console.log("Proxy: "+targeturl);
request({
url: targeturl,
method: req.method,
json: req.body
}, function (error, response, data) {
if(error) {
res.send(error);
res.end();
} else {
console.log("SUCCESS...");
console.log(data);
res.send(data);
res.end();
}
});
});
Our data is a two-level hierarchy of Flows and one or more child Milestones on each Flow. When someone clicks the Save link on our page to save a Flow and its Milestones, the Javascript pulls the Flow data from the page, then makes an AJAX call to save the Flow information first, then waits for the "done()" handler before pulling the Milestone information from the page and making a second AJAX call to save that data. Here is the saveFlow() function executed in the browser...
function saveFlow(organization_id, user_id, flow_id) {
var name = $('#Name').val();
var purpose = $('#Purpose').val();
console.log("Trying to save flow: " + flow_id);
if (name && name.length > 0) {
$.ajax({
url: "/api/flow",
type: 'POST',
datatype: 'json',
data: {
flow_id: flow_id,
name: name,
purpose: purpose,
organization_id: organization_id,
user_id: user_id,
timestamp: (new Date())
}
})
.fail(function (error) {
console.log("Could not save the flow: " + error.message);
})
.done(function (flow) {
console.log("Saving milestones for flow "+flow.id+" ["+flow_id+"]");
saveMilestones(flow.id);
});
} else {
console.log("Refusing to save a flow that has no name");
}
}
The proxy code shows me that this call succeeded...
SUCCESS...
{ success: true,
message: 'Flow Updated!',
id: '56de8e346d229b492a0954f9' }
But the proxy code also demonstrates that the next API call never takes place. Moreover, the log statement in the Done block is never executed, nor are any log statements placed inside the saveMilestones() function.
Clearly, the .done() handler is never being called in this case. I use the done() handler in other parts of my code and it works. I've compared the code but don't see any differences. The proxy code we're using is driving every other API call made from our pages successfully, so I don't think that it's failing to return the success status.
I've done a lot of searching on this site and tried every suggestion I could find - adding a explicit "json" datatype parameter, switching from ":success" to ".done()" - but nothing has worked yet. I even tried switching from done() to always() - a bad idea but I wanted to see if it would get called...and it wasn't called.
It's also worth noting that the .fail() handler is also not being executed.
Is it possible that the JSON block we're sending back to indicate success is somehow failing to make jQuery realize that we were successful? Or is it possible that the AJAX call is crashing when it tries to process the successful return? If so, how do I catch that to prove it and fix it?
Hope someone can help.
I have a backbone model which consist of following parse method.
parse: function(response, options){
// Handling Error for http response 200
if( response.Reasons && response.Reasons[0] ){
var errorResponse = {
responseJSON: {
reasons: [
{
code: response.Reasons[0].ReasonCode,
reason: ''
}
]
}
}
options.error( errorResponse );
return errorResponse;
}
}
My issue is, my web service won't give http response 500 for errors. I need to check it from the response & decide that. But the problem is, above code runs the error callback but after that it goes to the success callback as well. I need to stop that.
I receive a json response from the server, similar as following
{Data: {some data}}
for error
{Data: {}, Reasons: [{error code}]}
Please let me know how can i do that. Thanks in advance
Backbone by default expects the response to be JSON, so if JSON is returned from the server the success callback is triggered. With out overriding the fetch method of backbone, you can't stop from triggering success.
I'm trying to get info from my database via an AJAX request, but the success event isn't triggered.
The AJAX request is however received by the server, as it trigger the correct console log in the terminal.
I'm building it in Node, using the Express framework.
Here is the code for triggering the AJAX call. The 'componentDidMount triggered' is logged to the console. But then it stops. Neither the console.log in 'success' or 'error' is triggered, even though I know that the server has receenter code hereived an AJAX request.
app.get('/api/:name', function (req,res){
name = req.params.name;
Member.findOne({username: name}, function(err,member){
res.setHeader('Content-Type', 'application/json');
res.write(member.toString());
console.log('member fetched from DB: ', member);
res.end();
});
});
I'm not getting any other errors either, so I have no idea why this isn't working.
Can anybody please help?
UPDATE: Thanks for the help. I'm one step closer now. I did not realise I needed 'res.end' when serving the AJAX request. When I changed this, I've managed to get a response, though it's an error.
Her is the server side code:
app.get('/api/:name', function (req,res){
name = req.params.name;
Member.findOne({username: name}, function(err,member){
res.setHeader('Content-Type', 'application/json');
res.write(member.toString());
console.log('member fetched from DB: ', member);
res.end();
});
});
Tangential to your question, but equally important as figuring out why .success isn't being called: your code is not calling Main's setState in the success handler, because the success function isn't called "in your component" but "by jQuery, somewhere".
If you want to call setState on your component as part of the success handling, either cache the component and then call component functions on that:
var self = this;
$.ajax({
...
success: function() {
self.setState({ ... });
}
...
or use the JavaScript bind function (unrelated to jQuery.bind) to explicitly set the execution context for the success function to be your component:
...
success: function(data) {
console.log('success')
this.setState({data: data});
}.bind(this),
...
My mistake was in the res.write(member.toString()). I had to change it to res.write(JSON.stringify(member)), as the client was asking for a JSON file.
I have a page developed using backbone, handlebar and _.js. While fetching the model (as a normal http jsonp callback request), i do set options.error and options.success. The success callback gets called properly. However, to simulate the network down scenario, when i disable the network adapter in my PC (from control panel) and trigger the call again, the error callback is not getting called.
Could anyone please suggest if I am doing anything wrong?
Thanks and Best Regards,
V. Vasanth
Need look at the code to find out what's the problem. If you put callbacks to fetch method that should be right:
model.fetch({
success:function()
{
},
error:function()
{
}
});
If you override model sync method it could be like this:
sync:function(method, model, options)
{
$.ajax({
url:"your_url",
// some other configs
success:function(data)
{
options.success(data);
}
error:function()
{
options.error();
}
})
}
For some reason this.collection.create() is returning an error, but interestingly, the error message seems like the model I just added to my collection.
this.collection.create({
name: $('#big-input').val(),
firstRemark: $('#small-input').val(),
postedBy: window.user.displayName,
twitterHandle: window.user.twittername,
pictureUrl: window.user.profilePic
},{wait: true,
success: function(response){
console.log("success");
console.log(response)
},
error:function(err){
console.log(err)
}
});
this is what I get after console.log(err):
exports.newPost = function(req, res){
console.log("GOT NEW TWEET REQUEST");
var newPost = new Post(req.body)
newPost.dateCreated = new Date();
newPost.save();
res.send(200);
};
Thanks to answers below I was able to print my 'real' error. As seen below xhr.responseText is 'OK' and 'status' is 200. Why is this response triggering a success but an error?
I also have a parse method in my collection
parse: function(response){
this.page = response.page;
this.perPage = response.perPage;
this.total = response.total;
this.noofpages =response.noofpages;
return response.posts;
},
This is expected. Have a look at the Model.Save documentation, which says the error callback will be called with (model, xhr, options) as it's parameters.
If you want the actual contents of the response you can get it from the second parameters responseText property: xhr.responseText. There's more details on jqXHR elements in the jquery documentation: http://api.jquery.com/jQuery.ajax/#jqXHR
The parameters of your success callback are also not quite right - it takes (model, response, options)
EDIT:
Not entirely sure if it's the cause of your problems, but your server should be returning a 200 status code and the models JSON on success. Have a look at the Backbone.Sync documentation.
From looking at the code this does look important when passing wait: true as an option, as the attributes set are extended with the returned attributes from the server. Have a look at the options.success function used by backbone here to see what I mean. It certainly looks like something might go wrong if you return "OK" from the server, though I'm not sure if it'd be exactly the problem you're experiencing.
EDIT2: Slight correction to what I wrote above: The return value gets passed through Model.parse. Since you've defined a custom parse function, the server should return something that is going to work with it, rather than just plain JSON for the model.
The error function will receive 3 arguments as defined in the wrapError function in backbone.js
model.trigger('error', model, resp, options);
Therefore what your output is, is correct. You will want to add a variable to your error function to capture the response (2nd argument) and log that instead to help you debug.