2015-07-28 16 views
7

Mam scenariusz, w którym wywołanie modelu z modelu fetch() zwróci dane, z których właściwość będzie musiała zostać przekazana do innego interfejsu API, a typem zwracanym z tego interfejsu API będą faktycznie wymagane dane.Model szkieletowy: żądanie Ajax w analizie składniowej

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

Więc gdy Issue zostaną pobrane:

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

Będzie miał właściwość body zawierającej markdown składni tekstu, który z kolei trzeba przekazać do innego API i uzyskać tę odpowiedź, która zostaną przekazane do obejrzenia.

Jak widać z góry, próbowałem przesłonić parse, ale jego typem powrotu musi być obiekt, a fetch będzie async, więc co mogę zrobić, aby to zadziałało?

UWAGA: Wiem, że agregowanie danych na serwerze, a następnie odbieranie go będzie najlepszym pomysłem, ale nie jest to możliwe.

+0

'JSON.stringify (markdown.toJSON())' - to byłoby podwójne kodowanie, jesteś pewien, że chcesz? – Tomalak

+0

@ Tomalak Dopiero wtedy moja prośba przechodzi. Ale spróbuję jeszcze raz. Dzięki za przypomnienie. – lbrahim

Odpowiedz

4

Można zastąpić model sync method w modelu Issue, aby połączyć żądania.

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

Opcje hash przekazany do Model.sync zawiera wywołania zwrotne do model.parse, można go użyć, aby ustawić atrybuty modelu, kiedy jesteś zadowolony ze swoich danych.

A demo http://jsfiddle.net/puwueqe3/5/

+0

To wydaje się lepsze niż moja sugestia. +1 ode mnie. – ivarni

+0

@nikoshr Potrzebuję zarówno głównego obiektu, a następnie przeanalizowanego wyniku przeciążenia, aby można było zastąpić właściwość 'body'. Proszę zobaczyć ten skrzypek: http: // jsfiddle.net/ibrahimislam/puwueqe3/2/ – lbrahim

+1

@lbrahim Jakoś mi brakowało, że druga prośba zwróciła HTML, a nie JSON. Zaktualizowano – nikoshr

2

myślę, że trzeba zastąpić model na fetch aby uzyskać to do pracy

Zastanów się, co domyślny sprowadzić wygląda następująco:

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)

że realizacja nie będzie wspierać asynchroniczna wersja model.parse, ale ponieważ tworzysz klasę modelu przy użyciu .extend, możesz zastąpić ją własną implementacją, więc spójrzmy na to, co robi. Zajmuje obiekty options, tworzy wywołanie zwrotne success, a następnie deleguje do Backbone.Sync.

To wywołanie zwrotne, które wywołuje parse i to jest to, co należy zrobić, aby obsługiwać asynchroniczne.

Najszybszym i najbrudniejszym sposobem na to jest prawdopodobnie skopiowanie i zmodyfikowanie istniejącego domyślnego pobierania.

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

}); 

To tylko przykład tego, jak możesz spróbować rozwiązać ten problem. Nie testowałem go i może nie działać, ale nie widzę żadnych oczywistych powodów, dla których nie powinien.