Как заполнить модель Backbone из нескольких URL-адресов
Я хотел бы использовать одну модель Backbone, которая состоит из данных из разных конечных точек URL. Можно ли указать несколько URL-адресов в рамках одной модели? Я хотел бы избежать вызова AJAX вручную.
До сих пор я использовал jQuery Deferreds для вызова нескольких URL-адресов и синтеза их результатов в один объект.
Я могу представить два варианта: построение модели Backbone с частичной моделью для каждого URL-адреса или использование одного URL-адреса, а затем переопределение Model.fetch()
для вызова других URL-адресов. Но никто не делает меня удобным.
(Или я могу попытаться подкупить разработчика API, чтобы предоставить объединенную конечную точку URL...)
Ответы
Ответ 1
Я думаю, что оба способа, которые вы предложили, вполне разумны, но лично я проголосовал за подход fetch
.
Использование вложенных моделей имеет то преимущество, что работает "из коробки"; другими словами, используя одну модель для каждого сопоставления клиента <= > , вы избегаете изменять любые методы Backbone.Model
. Проблема с этим подходом заключается в том, что вы завершаете конгломерацию нескольких моделей.
Если это имеет смысл в любом случае (извлечение данных в сторону), то придерживайтесь вложенных моделей. Но если нет, вы вынуждаете остальную часть кода работать с несколькими моделями, а не только с целью сохранить код поиска данных простым. Если вы хотите просто усложнить код поиска данных и сохранить все остальное просто, вам лучше переписать fetch
.
fetch
делает одно, и это извлекает данные через взаимно однозначное сопоставление между ним и удаленным URL. Если вам требуется сопоставление one-to- many, то имеет смысл просто переопределить поведение по умолчанию fetch
. Кроме того, вы знаете, что достаточно безопасно переопределять fetch
, потому что это имя не _fetch
, а Backbone использует стиль подчеркивания, чтобы назвать его псевдо-частными методами (например, _validate
).
Если вам все еще не нравится любой из этих подходов, есть еще один вариант: request
события. В последней версии Backbone появилось новое событие request
, которое запускается всякий раз, когда запускается fetch
. Это означает, что вы можете настроить обработчик событий, который извлекает ваши вторичные данные в ответ на первичный запрос данных, например:
Backbone.Model.extend({
initialize: function() {
this.on('request', this.handleRequest);
_(this).bindAll('handleTriggeredRequestResponse');
},
handleRequest: function() {
$.ajax(url: secondDataUrl, complete: this.handleTriggeredResponse);
$.ajax(url: tertiaryDataUrl, complete: this.handleTriggeredResponse);
},
handleTriggeredResponse: function(response) {
this.set(response.data);
},
url: firstDataUrl
});
Ответ 2
Существует другое решение. Чтобы переопределить функцию Backbone.ajax по умолчанию с расширенным выполнением jQuery ajax, поддерживающим массив или хэш URL-адресов. Он будет загружать данные параллельно и передавать управление обратно в базовую модель после завершения всех запросов.
ajaxForArray = ->
options = _.first arguments
$.when.apply $, options.url.map (url) ->
$.ajax _.merge _.omit(options, ['url', 'success']), {url}
.done ->
resp =
if options.url.length > 1
_.slice(arguments).map (arg) -> _.first arg
else
_.first arguments
options.success? resp
ajaxForHash = ->
options = _.first arguments
pairs = _.transform options.url, (memo, url, key) ->
memo.push {key, url}
, []
$.when.apply $, pairs.map (pair) ->
$.ajax _.merge _.omit(options, ['url', 'success']), url: pair.url
.done ->
resp = _.slice(arguments).reduce (memo, arg, i) ->
memo[pairs[i].key] = _.first arg
memo
, {}
options.success? resp
Backbone.ajax = ->
options = _.first arguments
if _.isArray options.url
ajaxForArray.apply $, arguments
else if _.isObject(options.url) and not _.isFunction options.url
ajaxForHash.apply $, arguments
else
$.ajax.apply $, arguments
Теперь модели могут быть установлены с массивом URL-адресов, например
class ArrayBasedModel extends Backbone.Model
url: ['/aoo', '/boo', '/coo']
parse: (resp) ->
super _.extend {}, resp[0], resp[1], resp[2]
или хеш URL-адресов, например
class HashBasedModel extends Backbone.Model
url: {aoo: '/aoo', boo: '/boo', coo: '/coo'}
parse: (resp) ->
super _.extend {}, resp.aoo, resp.boo, resp.coo
поэтому, когда parse() переопределяется, просто добавьте базовую логику слияния результатов от всех вызовов ajax, чтобы передать ее в Backbone.Model.parse().
После этого свойства из разных URL-адресов будут доступны в одном атрибуте модели.