1

I'm building a Rails 4 application and I'm trying to combine location.hash Backbone History with turbolinks push state. The application is broken up into multiple smaller, SPA like pages.

Example of Problem

The problem I'm having is that when I do Backbone.Router.navigate(), within a page, it doesn't register anything with turbolinks. Here's a hypothetical example to demonstrate the problem:

  1. Visit /one
  2. @router.navigate 'page-one'
  3. Location becomes /one#page-one
  4. @router.navigate 'page-two'
  5. Location becomes /one#page-two
  6. Click on link to /two
  7. Turbolinks intercepts and navigates to /two
  8. Click on the Back Button, you are still stuck on /two
  9. Click on the Back Button again, you are still stuck on /two
  10. Click on the Back Button a third time, you are brought back to /one

What I Tried

I guessed that what's probably happening is that when navigating via backbone, the browser is logging the change but turbolinks isn't. I tried to expose Turbolinks.reflectNewUrl:

turbolinks.js.coffee

@Turbolinks = { visit, pagesCached, reflectNewUrl }

And then modifying Backbone.Router.navigate so that it registers with turbolinks every time we navigate:

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

That sort of worked, Step 8 above is no longer stuck on /two, but click on the back button again and we get this error:

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

So something is still wrong.

Now I'm hoping there's a kind soul out there to help me out and point out my perhaps obvious blunder :)

4

2 回答 2

3

我不同意不混合 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()
于 2014-05-15T15:23:17.960 回答
-1
window.Summerland =
  Models: {}
  Collections: {}
  Views: {}
  Routers: {}
  initialize: ->
    new Summerland.Routers.Application
    Backbone.history.start pushState: true

$(document).ready ->
  Summerland.initialize()

$(document).on 'page:load', ->
  Backbone.history.stop()
  Summerland.initialize()
于 2013-12-30T07:56:25.737 回答