2015-07-28 25 views
7

Ho uno scenario in cui una chiamata fetch() restituisce dati da cui una proprietà dovrà essere passata a un'altra API e il tipo restituito da tale API sarà effettivamente i dati richiesti.: richiesta Ajax in override di parsing

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' 
}); 

Così, quando viene prelevato un Issue:

var issue = new Issue({id: 1}); 
issue.fetch().then(function(){ 
    //do stuff 
}); 

avrà una proprietà di body contenente Markdown testo sintassi che a sua volta ho bisogno di passare ad un altro API e ottenere la che la risposta che verrà passato in rassegna.

Come si può vedere da sopra, ho provato sovrascrivendo parse ma il suo tipo di ritorno deve essere un oggetto e fetch sarò async così che cosa posso fare qui per fare questo lavoro?

NOTA: So che aggregare i dati nel server e quindi riceverli sarà l'idea migliore ma non è possibile atm.

+0

'JSON.stringify (markdown.toJSON())' - che sarebbe doppia codifica, sei sicuro si desidera che? – Tomalak

+0

@Tomalak Solo dopo che la mia richiesta è passata. Ma ci riproverò. Grazie del promemoria. – lbrahim

risposta

4

È possibile eseguire l'override di sync method nel modello Issue per concatenare le richieste.

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; 
      }); 
     }); 
    } 
}); 

Le opzioni hash passati al Model.sync contiene i callback per model.parse, è possibile utilizzarlo per impostare gli attributi sul modello quando si è soddisfatti con i vostri dati.

e una demo http://jsfiddle.net/puwueqe3/5/

+0

Questo sembra migliore del mio suggerimento. +1 da me. – ivarni

+0

@nikoshr Ho bisogno sia dell'oggetto principale che del risultato del markdown così da poter sostituire la proprietà 'body'. Si prega di vedere questo violino: http: // jsfiddle.net/ibrahimislam/puwueqe3/2/ – lbrahim

+1

@lbrahim In qualche modo ho mancato che la seconda richiesta restituisse HTML e non JSON. aggiornato – nikoshr

2

penso che si dovrebbe ignorare del modello fetch per arrivare a questo lavoro

in considerazione ciò che il default recuperare assomiglia:

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)

che l'attuazione non sosterrebbe una versione asincrona di model.parse, ma dal momento che si crea una classe del modello usando .extend è possibile sovrascriverlo con la propria implementazione in modo da dare un'occhiata a ciò che fa. Sono necessari oggetti options, viene creata una richiamata success e successivamente delegata a Backbone.Sync.

È quella richiamata che chiama parse e questo è ciò che deve essere fatto per supportare asincrono.

Il modo più veloce e più sporco per farlo è probabilmente quello di copiare e modificare il recupero predefinito esistente.

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); 
    } 

}); 

Questo è solo un esempio di come si potrebbe provare a risolvere questo. Non l'ho provato e potrebbe non funzionare, ma non vedo ragioni immediatamente ovvie perché non dovrebbe.