I have a backbone model which has custom parse function, while calling the fetch on this model sometimes I wanted to skip the parse function in certain scenarios. How can I do it. I tried the following option which did not work.
myModel.fetch({
parse: false,
success: _.bind(function(model, response) {}, this),
error: _.bind(function(model, response) {}, this)
});
My model code:
var MyModel = BaseModel.extend({
initialize: function() {
console.log('EventCloneModel in initialize()');
_.extend(Backbone.Model.prototype, Backbone.Validation.mixin);
},
url: function() {
var url = gc.apiUrl;
var locale = "en_US"
url += '&locale=' + locale;
return url;
},
parse: function(response) {
//some parsing logic goes here
return response;
},
getValidations: function(){
return this.validation;
}
});
return MyModel;
});
Put the skip condition in your parse function. How you determine the skip condition is up to you.
parse: function(response) {
if(skipParse)
return response;
//parse the response here. If the code reaches this point,
//it means you want to parse it.
return response;
},
Related
My script:
var testApp = (function($){
var data = [{
"layout": "getSample",
"view": "conversations",
"format": "json",
}];
var Data = 'default';
function ajaxCall(opt) {
return new Promise(function(resolve, reject) {
jQuery.ajax({
method: "POST",
url: localStorage.getItem("root")+"/index.php",
"data": opt,
error: function() {
alert('error');
},
success: function(result) {
console.debug(result);
resolve(result);
}//end success
});//end ajax
});//end promise
}
return {
render: function(opt) {
if(typeof opt === 'object') {
var list = {
data : [opt]
}
//here I'm passing list object's data to be used in ajaxCall function.That's the reeason I used call method. It's data is passed from another page.
ajaxCall.call (list, list.data).then(function(v) {
console.log("v "+v); // nothing happens yet...expecting for the success object to be passed here
}).catch(function(v) {
//nothing to do yet
});
}
}
};//end return
})(jQuery);
Is the correct way of using promise with ajax?
ajaxCall.call (list, list.data).then(function(v) {
console.log("v "+v); // doesn't return anything
}).catch(function(v) {
//nothing to do yet
});
referred: How do I return the response from an asynchronous call?
well, its a simple fix I found..
//below line of code, moved above return new promise and it worked
var opt = jQuery.extend({}, data[0], opt[0]);
jQuery Ajax functions already return promises. You don't have to turn them into promises manually.
var testApp = (function($) {
var ajaxDefaults = {
"layout": "getSample",
"view": "conversations",
"format": "json",
};
// this can be re-used in all your Ajax calls
function handleAjaxError(jqXhr, status, error) {
console.error('Ajax error', error);
});
function ajaxCall(opt) {
var url = localStorage.getItem("root") + "/index.php",
data = jQuery.extend({}, ajaxDefaults, opt);
return $.post(url, data).fail(handleAjaxError);
}
return {
render: function(opt) {
return ajaxCall(opt).then(function (result) {
console.log("v " + result);
return result;
});
}
};
})(jQuery);
You don't need to use .call() to call a function. It doesn't make a lot of sense in this case, either. If it is not an object method, but a stand-alone function, call it normally and pass arguments.
There is no guarantee that localStorage.getItem("root") contains any value, but your code ignores this possibility. That's a bug.
You don't want two variables data and Data in your code. Don't set up trip wires like this.
There is no need to use $.ajax() when $.post()/$.get() can do the job in one line.
Return something from your render() method and from its .then() handler, too, so you can chain more code elsewhere in your application, e.g.
app.render({data: 1234}).then(function (result) {
// ...
});
I have a scenario where a fetch() call of a model will return data from which a property will need be passed to another API and return type from that API will be the actually required data.
var Issue = Backbone.Model.extend({
urlRoot: 'https://api.github.com/repos/ibrahim-islam/ibrahim-islam.github.io/issues',
parse: function(response, options){
var markdown = new Markdown({ text : response.body });
markdown.fetch({
contentType: 'application/json',
type: 'POST',
data: JSON.stringify( markdown.toJSON() ),
success: function(data){
response.body = data;
}
});
return response;
}
});
var Markdown = Backbone.Model.extend({
defaults:{
'text': '',
'mode' : 'markdown'
},
url: 'https://api.github.com/markdown'
});
So, when an Issue will be fetched:
var issue = new Issue({id: 1});
issue.fetch().then(function(){
//do stuff
});
It will have a property of body containing markdown syntax text which in turn I need to pass to another API and get the that response which will be passed down to view.
As can be seen from above, I tried overriding parse but its return type has to be an object and fetch will be async so what can I do here to make this work?
NOTE: I know aggregating the data in server and then receiving it will be best idea but that is not possible atm.
You could override the sync method in your Issue model to chain your requests.
var Issue = Backbone.Model.extend({
urlRoot: 'https://api.github.com/repos/ibrahim-islam/ibrahim-islam.github.io/issues',
sync: function(method, model, options) {
if (method !== 'read')
return Backbone.sync.apply(this, arguments);
// first request
var xhr = Backbone.ajax({
type: 'GET',
dataType: 'json',
url: _.result(model, 'url')
});
// second request
return xhr.then(function (resp1) {
var markdown = new Markdown({text : resp1.body || 'body'});
var data = markdown.toJSON();
// the callback to fill your model, will call parse
var success = options.success;
return Backbone.ajax({
url: _.result(markdown, 'url'),
dataType: 'html',
contentType: 'application/json',
type: 'POST',
data: data
}).then(function(resp2) {
// sets the data you need from the response
var resp = _.extend({}, resp1, {
body: resp2
});
// fills the model and triggers the sync event
success(resp);
// transformed value returned by the promise
return resp;
});
});
}
});
The options hash passed to Model.sync contains the callbacks to model.parse, you can use it to set the attributes on your model when you're satisfied with your data.
And a demo http://jsfiddle.net/puwueqe3/5/
I think you would have to override the model's fetch to get this to work
Consider what the default fetch looks like:
fetch: function(options) {
options = _.extend({parse: true}, options);
var model = this;
var success = options.success;
options.success = function(resp) {
var serverAttrs = options.parse ? model.parse(resp, options) : resp;
if (!model.set(serverAttrs, options)) return false;
if (success) success.call(options.context, model, resp, options);
model.trigger('sync', model, resp, options);
};
wrapError(this, options);
return this.sync('read', this, options);
},
(github)
That implementation would not support an async version of model.parse, but since you create a model class using .extend you can override this with your own implementation so lets look at what it does. It takes an options objects, creates a success callback and then delegates to Backbone.Sync.
It's that callback that calls parse and that's what needs to be made to support async.
The quickest, dirtiest way to do this is probably to just copy and modify the existing default fetch.
var MyModel = Backbone.Model.extend({
fetch: function(options) {
options = _.extend({parse: true}, options);
var model = this;
var success = options.success;
options.success = function(resp) {
function parser(resp, options, cb) {
...do your async request stuff and call cb with the result when done...
}
parser(resp, options, function(result) {
if (!model.set(result, options)) return false;
if (success) success.call(options.context, model, resp, options);
model.trigger('sync', model, resp, options);
});
};
wrapError(this, options);
return this.sync('read', this, options);
}
});
This is just an example of how you might try to solve this. I've not tested it and it might not work but I don't see any immediately obvious reasons why it shouldn't.
I am trying to write a JavaScript Object which has many Properties and Methods. The basic function of this code is to send an ajax call and get data from server.
Code IS:
function restClient(options) {
var _response;
var _response_status;
var _response_message;
var _response_data;
// Default Options
var _default = {
restCall: true,
type: "GET",
url: '',
contentType: "application/json; charset=utf-8",
crossDomain: false,
cache: false,
dataType: 'json',
data: {},
beforeSend: _ajaxBeforeSend,
success: _ajaxSuccess,
error: _ajaxError,
complete: _ajaxComplete
};
// Extend default Options by User Options
var ajaxOptions = $.extend(_default, options);
// Private Methods
function _ajaxBeforeSend() {
}
function _ajaxSuccess(response) {
_response = response;
_response_status = response.status;
_response_message = response.message;
_response_data = response.data;
}
function _ajaxError(xhr, status, error) {
_response_status = xhr.status;
_response_message = error;
}
function _ajaxComplete(xhr, status) {
}
// Send Ajax Request
this.sendRequest = function() {
$.ajax(ajaxOptions);
};
// Get Server Response Pack [status,message,data]
this.getResponse = function() {
return _response;
};
// Get Server Response Status: 200, 400, 401 etc
this.getStatus = function() {
return _response_status;
};
// Get Server Message
this.getMessage = function() {
return _response_message;
};
// Get Server Return Data
this.getData = function() {
return _response_data;
};
}
Now I am trying to create object using new operator and call sendRequest(); method to send an ajax call and then I am calling getResponse(); to get server response like:
var REST = new restClient(options);
REST.sendRequest();
console.log(REST.getResponse());
Every thing is working properly But the problem is REST.getResponse(); call before to complete Ajax which give me empty result. If i do like this
$(document).ajaxComplete(function(){
console.log(REST.getResponse());
});
then it work But Still two problems are
If there are another ajax call its also wait for that
its looking bad I want to hide this ajaxComplete() some where within restClient();
Please Help me.
Thanks.
You have to change method sendRequest to accept a callback, that you'll call on response completion.
this.sendRequest = function(cb) {
this.cb = cb;
$.ajax(ajaxOptions);
};
this._ajaxComplete = function(xhr, status) {
this.cb && this.cb();
}
Also, after defining this._ajaxComplete change the _default.complete handler, in order to bind the this object, otherwise you'll miss the cb property:
_default.complete = this._ajaxComplete.bind(this);
Your client code will become:
var REST = new restClient(options);
REST.sendRequest(function(){
console.log(REST.getResponse());
});
I have the following function:
loadMsgBody: function (id) {
return dojo.xhrGet({
url: "myurl",
handleAs: "text",
content: {
id: id
},
load: function (response) {
return response;
},
error: function (response) {
alert(response);
}
});
}
And calling it:
var text = "";
this.loadMsgBody(this.msgId).then(function (response) {
text = response;
});
Now I expect to get the return value of the function but instead I am getting an empty value for text. However, in Firebug I do see the response from the server with the correct value. I've searched and found these links : DOJO xhrGet how to use returned json object?
and:
Using hitch / deferred with an xhrGet request
But I still can't get and store the data with the above code. I don't want to do the manipulation inside the xhrGet call, I want to retrieve the data and use as it will be used multiple times.
Is there anything I am missing?
Dojo's XHR methods return instances of the class dojo/Deferred, because they are asynchronous. What this means is that the functions returns before the value of the response is available. In order to work with the results of the asynchronous response you need to wait for it to return. Dojo exposes this using a uniform API, Deferreds. Instances of the dojo/Deferred class have a method then. The then method takes a function as a parameter. That function will execute once the Deferred have been resolved (in this case, when the request has completed).
var deferred = loadMsgBody();
deferred.then(function(response){
//work with response
});
I would try changing your load function to evoke your callback function:
loadMsgBody: function (id, callback) {
return dojo.xhrGet({
url: "myurl",
handleAs: "text",
content: {
id: id
},
load: function (response) {
if(callback) {
callback(response);
}
},
error: function (response) {
alert(response);
}
});
}
Try this:
loadMsgBody: function (id, callback) {
return dojo.xhrGet({
url: "myurl",
handleAs: "text",
content: {
id: id
},
load: function (response) {
callback.apply(null,[response]);
},
error: function (response) {
alert(response);
}
});
}
Then:
var text = "";
this.loadMsgBody(this.msgId, function (response) {
text = response;
console.log("text:",text); // this will show your return data
});
console.log("text:",text); // this will show empty data because ajax call is asynchrize, at this time , data not return yet.
setTimeout(function(){
console.log("text:",text); // this will show your return data again because ajax call should have finished after 30000 ms
},30000)
I have a method below:
self.getOrAddCache = function (key, objectFactory) {
var data = self.getFromCache(key);
if (!data) {
data = objectFactory();
if (data && data != null)
self.addToCache(key, data);
}
return data;
};
I use like this:
function getCities()
{
var cities = getOrAddCache(CacheKeys.Cities, function() {
var cityArray = new Array();
// get city informations from service
$.ajax({
type: "GET",
async: true,
url: "service/cities",
success: function (response) {
$.each(response, function(index, value) {
cityArray.push({
name: value.name,
id: value.id
});
});
}
});
if (cityArray.length > 0)
return cityArray;
else {
return null;
}
});
return cities;
}
getCities function always return null because getCities not waiting for completion async ajax request.
How can i resolve this problem? (Request must be async)
The best solution for this is to use Deferred objects. Since you require your AJAX call to be asynchronous, you should have your getCities function return a promise to return that data at some point in the future.
Instead of storing the raw data in the cache, you store those promises.
If you request a promise that has already been resolved, that will complete immediately. If there's already a pending request for the cached object, the async AJAX call will be started and all outstanding callbacks waiting for that promise will be started in sequence.
Something like this should work, although this is of course untested, E&OE, etc, etc.
self.getCached = function(key, objectFactory) {
var def = self.getCache(key);
if (!def) {
def = objectFactory.call(self);
self.addToCache(key, def);
}
return def;
}
function getCities() {
return getCached(CacheKeys.Cities, function() {
return $.ajax({
type: 'GET',
url: 'service/cities'
}).pipe(function(response) {
return $.map(response, function(value) {
return { name: value.name, id: value.id };
});
});
});
}
Note the usage of .pipe to post-process the AJAX response into the required format, with the result being another deferred object, where it's actually the latter one that gets stored in your cache.
The usage would now be:
getCities().done(function(cities) {
// use the cities array
});
With a callback:
function getCities(callbackFunction)
{
var cities = getOrAddCache(CacheKeys.Cities, function() {
var cityArray = new Array();
// get city informations from service
$.ajax({
type: "GET",
async: true,
url: "service/cities",
success: function (response) {
$.each(response, function(index, value) {
cityArray.push({
name: value.name,
id: value.id
});
});
callbackFunction(cityArray);
}
});
});
}
getCities(function(cityArray){
// do stuff
});
You can't return the result from a function fetching asynchronously the data.
Change your getCities function to one accepting a callback :
function fetchCities(callback) {
var cities = getOrAddCache(CacheKeys.Cities, function() {
var cityArray = new Array();
// get city informations from service
$.ajax({
type: "GET",
async: true,
url: "service/cities",
success: function (response) {
$.each(response, function(index, value) {
cityArray.push({
name: value.name,
id: value.id
});
});
if (callback) callback(cityArray);
}
});
});
}
And use it like this :
fetchCities(function(cities) {
// use the cities array
});
Note that it's technically possible, using async:true, to make the code wait for the response but don't use it : that's terrible practice and it locks the page until the server answers.
You seem to be contradicting yourself.
Something that is asynchronous, by definition, does not pause the script to wait for the end of it's execution. If it does wait, it cannot be asynchronous.
The best wayto fix this is by adding a callback function in your ajax success function that passes the end result to another function, which handles the rest of the execution.