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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With