Rails: гибридная маршрутизация Backbone + Turbolinks

Я создаю приложение Rails 4 и пытаюсь объединить историю магистральной сети location.hash с состоянием push-управления turbolinks. Приложение разбито на несколько меньших страниц, похожих на SPA.

Пример проблемы

У меня проблема в том, что когда я делаю Backbone.Router.navigate() на странице, она ничего не регистрирует с турбоссылками. Вот гипотетический пример, демонстрирующий проблему:

  1. Посетите /one
  2. @router.navigate 'page-one'
  3. Местоположение становится /one#page-one
  4. @router.navigate 'page-two'
  5. Местоположение становится /one#page-two
  6. Нажмите на ссылку /two
  7. Turbolinks перехватывает и переходит к /two
  8. Нажмите кнопку «Назад», вы все еще застряли на /two
  9. Нажмите кнопку «Назад» еще раз, вы все еще застряли на /two
  10. Нажмите кнопку «Назад» в третий раз, и вы вернетесь к /one.

Что я пробовал

Я предположил, что, вероятно, происходит то, что при навигации по магистрали браузер регистрирует изменение, а турбоссылки — нет. Я попытался разоблачить Turbolinks.reflectNewUrl:

turbolinks.js.coffee

@Turbolinks = { visit, pagesCached, reflectNewUrl }

А затем модифицируем Backbone.Router.navigate так, чтобы он регистрировался в турболинках каждый раз, когда мы перемещаемся:

navigate = Backbone.Router.prototype.navigate
Backbone.Router.prototype.navigate = (page, args...) ->
  navigate page, args...
  window?.Turbolinks?.reflectNewUrl "##{page}"

Это вроде сработало, Шаг 8 выше больше не зависает на /two, но снова нажмите кнопку "Назад", и мы получим эту ошибку:

Uncaught TypeError: Cannot call method 'getElementsByTagName' of null turbolinks.js:142
removeNoscriptTags turbolinks.js:142
changePage turbolinks.js:111
fetchHistory turbolinks.js:69
(anonymous function) turbolinks.js:390

Значит что-то все же не так.

Теперь я надеюсь, что есть добрая душа, которая поможет мне и укажет на мою, возможно, очевидную ошибку :)


person Nathan Kot    schedule 13.09.2013    source источник
comment
Почему вы пытаетесь смешивать две разные (и, вероятно, несовместимые) системы одностраничных приложений?   -  person mu is too short    schedule 13.09.2013
comment
сначала это было обычное приложение для рельсов, но теперь мы смотрим на создание системы отображения разума в одном из представлений — мы хотим иметь возможность перемещаться по истории в разные точки на карте. рад отказаться от турболинков, если нет альтернативы, но было бы неплохо сохранить оба   -  person Nathan Kot    schedule 13.09.2013
comment
Backbone и Turbolinks принципиально несовместимы из-за того, как они взаимодействуют с push-состоянием браузера. Кроме того, Turbolinks — это, по сути, прокладка для ускорения загрузки полных HTML-страниц, а Backbone — это уровень моделирования данных, который может более непосредственно взаимодействовать с вашим приложением. Они совершенно разные, поэтому я бы не стал их объединять.   -  person Tim Dorr    schedule 13.09.2013
comment
Почему вы не можете использовать маршрутизаторы Backbone и pushState, чтобы иметь возможность перемещаться по истории в разные точки на карте?   -  person mu is too short    schedule 13.09.2013
comment
muistooshort, я использую магистральные маршрутизаторы, хотя с отключенным состоянием pushstate его включение создает больше сложностей, поскольку магистральной сети необходимо знать о маршрутах rails — и все еще возникает та же проблема после попытки с включенным состоянием push. Тимдорр, наверное, ты прав. Я надеюсь, что, поскольку маршрутизация Backbone может использовать location.hash, а не pushState, есть способ обойти это. Не уверен, актуальна ли эта проблема с GH: github.com/rails/turbolinks/issues/256   -  person Nathan Kot    schedule 13.09.2013


Ответы (1)


Я не согласен с тем, что нельзя смешивать Turbolinks и Backbone. Они выполняют разные роли, и бывают случаи, когда вы можете использовать оба.

На самом деле, я понимаю, что Basecamp использует и то, и другое.

Вы можете использовать этот (ныне печально известный) пост в качестве отправной точки: http://www.goddamnyouryan.com/blog/rails-4-turbolinks-and-backbone

Однако у меня все еще была проблема, с которой вы столкнулись. Вот как я это решил (предварительно, я не проверял это в дикой природе):

window.MyApp =
  Models: {}
  Collections: {}
  Views: {}
  Routers: {}

  initialize: ->
    # Build it up
    @router = new MyApp.Routers.Pages(data)
    Backbone.history.start()

  close: ->
    # Tear it down
    @router.close(false)
    @router = undefined

# Turbolinks exposes the before-change event when a page change is initiated
$(document).on 'page:before-change', ->
  # If the app has been initialized
  if MyApp.router?

    # Stop the history and clean up the app
    Backbone.history.stop()
    MyApp.close()

    # We're leaving the domain of the backbone app, check for Turbolinks
    if Turbolinks?.supported
      # Push the last known state of the app to the Turbolinks history
      window.history.replaceState {turbolinks: true, url: window.location.href}, window.title, window.location.href

$(document).on 'page:change', ->
  # If it's the page with our backbone app
  if $('#pages').size() > 0
    MyApp.initialize()
person John H    schedule 15.05.2014
comment
Спасибо за объяснение. Я пробовал это, но не мог заставить его работать. Есть две вещи, с которыми я сталкиваюсь: 1. Backbone.Router, похоже, не имеет метода close(). Должен ли я использовать Marionette.AppRouter? 2. При переходе между основными и неосновными страницами я получаю сообщение об ошибке: Uncaught Error: Backbone.history has already been started Кто-нибудь еще сталкивался с этим? - person Remon Oldenbeuving; 20.06.2014
comment
@router.close() в этом контексте — это просто любой близкий метод, который вы используете для очистки вашего маршрутизатора. Вы можете определить его самостоятельно или не указывать, если хотите. - person John H; 21.06.2014
comment
Я немного рассказал об этом и попытался объяснить, почему это работает, в блоге здесь: medium.com/@midhir/ - person John H; 28.06.2014