Backbone Create - javascript

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.

Related

JS - Getting either text or JSON with Fetch API

I am moving over from jQuery AJAX requests to the new Fetch API (nothing against jQuery, I still have it in my site, but Fetch looks - according to Jake Archibald and David Walsh and also IMHO - to be the new way of sending async requests).
As such, with jQuery, I had the following function (more or less):
function ajaxCall(type, url, data) {
return $.ajax({
type: type,
url: url,
data: data,
})
.fail(function(xhr, status, errorThrown) {
// Do fail stuff
})
.always(function(xhr, status) {
// Do always stuff
});
}
// Later...
var myAjax = ajaxCall(myType, myUrl, myData);
myAjax.done(function(xhr) {
// Do done stuff
});
This way, I could have one function be called to handle any and all ajax requests I could ever need (for the most part at least...). Note that I do not declare a dataType, as I use jQuery's intelligent guess. This way my server can send me whatever response and I could handle it (probably a smarter way to do this would be to pass another parameter with the data type - in the case the "intelligent guess" goes wrong, but this was the way I set it up).
I am now trying to recreate the above with the new Fetch API. What I have so far currently looks like this:
function fetchCall(url, method, body) {
// This if statement is supposed to handle
// query selectors (which in GET requests go in the url)
// on GET requests - as opposed to POST req's which go in the body
if (method === 'GET') {
var data = body;
url = new URL(url, location.protocol + '//' + location.host + '/');
Object.keys(data).forEach(key => url.searchParams.append(key, data[key]));
body = undefined;
}
return fetch(url, {
method: method,
body: body
}).then(function(res) {
if (res.ok) return res;
throw new Error('Server error. Status code: ', res.status);
}).catch(function(err) {
console.log(err);
});
}
// Later...
var myFetch = fetchCall(myUrl, myMethod, myBody);
myFetch.then(function(res) {
console.log(res);
});
The problem I am running into is that if res.ok return res; does not state what type of response it is (i.e. res.json(), res.blob(), res.text(), etc.).
Thus, I am wondering how to set up a dynamic way of setting the type of response body. Is this even possible at the Fetch API's current state of development? Is it just that there is something I am not duplicating in MDN?
After messing around with this, I also realized I could make it always set to return res.text(); and the if the call is supposed to be JSON, use JSON.parse(response);, but I do want it to be dynamic. What if I end up wanting to return a blob()?
So, as far as the conversation has reached, there is a way to understand what type of content has been received, with two remarks:
Typically you have to always know and expect exact content type, and a universal solution is rather odd in case of fetching from a certain remote endpoint, and
The Content-Type header is what will tell you the type of content received, but the server may send a wrong header, which is very unusual to happen and therefore is negligible.
The Response object has header property that is (kind of) a Map, so you can use its get method to get a value by key.
The easiest and cleanest way to check if the returned value is a certain MIME type you expect is by using a regular expression:
// replace url with the actual API endpoint URL
fetch(url).then(response => {
const contentType = response.headers.get('Content-Type'); // -> "text/html; charset=utf-8"
if (/text\/html/i.test(contentType)) {
// do something when the Content-Type is text/html
} else if (/application\/json/.test(contentType)) {
// do something when the Content-Type is application/json
}
// and so on, for every Content-Type you need.
}).catch(error => {
// do something when error happens
});

Backbone Response on Sync

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

Abort accessing success callback in backbone model

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.

AJAX request in React JS js doesn't work

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.

After creating new instance in collection, don't do a GET request on the endpoint (backbone)

After I add a model instance to a collection, I do a POST request to add it. Then a GET request is done to get the model from the server. Is there a way to not to the GET request, only the POST request? Also, is it possible to get the success and error callback functions to respond to the success and failure of the POST request?
I want to do this because the collection has a URL that parses the JSON data that gets back, so the GET request doesn't work, but the POST request does work. I don't want to do a GET request on a endpoint that doesn't work.
The GET request is unnecessary. On the server in your POST handler you should return a JSON result back to the client representing the model. This is especially useful when there are generated fields such as an id. Then on the client in the success callback you can grab the model returned from the POST.
In the following example a new model is added to the collection if successful. I've also included the error callback which will fire if either client side validation fails or the POST fails:
var isNew = this.model.isNew();
this.model.save({}, {
success: function(model, response) {
if (isNew && this.collection) {
this.collection.add(model);
}
},
error: function(model, response) {
var errorMsg;
// Response may be string (if failed client side validation or an AJAX response (if failed server side)
if (_.isString(response))
errorMsg = response;
else
errorMsg = response.responseText;
}
});
The process you follow is indeed unnecessary. You should be using create on the collection to directly add the model, and invoke sync (the POST in this case) in the same time.
For example:
collection.create({foo: 'bar'}); or collection.create(unsaved_model);
The result of invoking create will return either the (saved) model or false if this was not successful. In addition it is possible to wait for the model to be saved before adding to the collection by doing
collection.create({foo: 'bar'}, {wait: true});
The documentation is your friend.

Categories

Resources