Магистральные маршрутизаторы: дождаться получения данных сначала

Я думаю, что я не совсем понимаю, как правильно использовать маршрутизаторы Backbone. Вот что я получил:

У меня есть некоторые данные, которые я извлекаю с сервера, когда страница загружается, а затем упаковывает ее в модели и коллекции. Количество этих моделей и коллекций является неопределенным. Я хочу использовать маршрутизатор, чтобы иметь возможность отображать представление определенных коллекций непосредственно с самого начала.

Проблема заключается в следующем: базовый маршрутизатор запускается раньше, и поскольку я прошу его получить доступ к определенному виду и вызвать его действие render, он не сможет этого сделать, потому что эти представления еще не созданы. Это означает, что мне действительно нужно запустить мои маршруты после завершения выборки.

Я не знаю, подходит ли это для этого, но единственная идея, которую я придумал, заключается в следующем:

  • Оберните определение маршрутов и бит Backbone.history.start(); в отдельную функцию, доступную на верхнем уровне (например, подготовьтесь к ее вызову вручную позже).
  • Запустите эту функцию как обратный вызов success для моих коллекций fetch()
  • Количество этих коллекций неизвестно, также я не могу узнать, когда все они были извлечены, и я не хочу запускать маршруты более одного раза. Поэтому я использую _.defer() и _.once().

Это работает, но это выглядит очень странно:

Маршрутизаторы:

    window.startRoutes = _.once(function() {

        var AccountPage = Backbone.Router.extend({

          routes: {
            'set/:id': 'renderSet',
          },

          renderSet: function(setId) {

              /** … **/

              // Call the rendering method on the respective CardView
              CardsViews[setId].render();

          }

        });

        var AccountPageRouter = new AccountPage;

        Backbone.history.start();

    });

Коллекция:

window.CardsCollection = Backbone.Collection.extend({

    model: Card,

    initialize: function(params) {

        /** … **/

        // Get the initial data
        this.fetch({success: function() {
            _.defer(startRoutes);
        }});

    },

});

Итак, мой вопрос... я все правильно? Или есть лучший способ сделать это (должно быть)?

Ответ 1

Вы можете определить свой маршрутизатор раньше времени; он ничего не сделает, пока не назовете Backbone.History.start().

Вы можете связать событие "reset" в своей коллекции, чтобы начать историю следующим образом:

my_collection.bind("reset", _.once(Backbone.History.start, Backbone.History))

Затем маршрутизатор начнет делать вещи, когда ваша коллекция будет полностью загружена. Я не уверен, что это именно то, что вы ищете (поскольку вы упомянули о наличии переменного количества коллекций).

У меня есть аналогичная ситуация, за исключением того, что я заранее знаю, какие коллекции я хочу загрузить до начала маршрутизации. Я добавил метод startAfter для своего маршрутизатора, например:

  window.Workspace = new (Backbone.Router.extend({
    . . .
    startAfter: function(collections) {
      // Start history when required collections are loaded
      var start = _.after(collections.length, _.once(function(){
        Backbone.history.start()
      }))
      _.each(collections, function(collection) {
        collection.bind('reset', start, Backbone.history)
      });
    }
  }));

а затем после того, как я установил свои коллекции

  Workspace.startAfter([collection_a, collection_b, ...])

Это может быть адаптировано для работы с автономными моделями, хотя я думаю, что вам нужно привязать к чему-то другому, кроме события reset.

Я рад, что прочитал ваш пример кода, использование _.once и _.defer указало мне в правильном направлении.

Ответ 2

Я просто проверяю мой метод .render(), чтобы все необходимые поля заполнялись, прежде чем использовать его. Если он еще не заполнен - ​​я создаю виджет "Загрузка...".

И все мои взгляды подписаны на изменения модели, this.model.bind('change', this.render, this);, поэтому сразу после загрузки модели снова будет вызываться render().