spine / spine

Lightweight MVC library for building JavaScript applications

Home Page:http://spine.github.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Spine.Route.setup({redirect: function}) does not work on setup()

jeremyhaile opened this issue · comments

For example:
redirect = -> console.log("REDIRECTING")
Spine.Route.setup({redirect: true})

If you add this and load the page with a non-existent route, the redirect function is never invoked. But the redirect function IS called if you try to navigate after the page is loaded.

This has the effect of allowing non-existent routes to break my application if they happen on initial page load.

Actually, upon closer inspection, it appears the "redirect" behavior only applies when Spine.Route.navigate is called explicitly. It never occurs on page load or on hashchange.

Is this intended behavior? I most commonly run into invalid routes when URLs get munged somehow in the wild. So the "redirect" option is most useful to me on page load.

interesting. I can see why it is an issue, but wonder why I haven't ever run into this... on the one had it should be fixable, on the other it isn't a redirect in some sense. need to think some more about this.

OK - what's the argument about it working that way? It was pretty surprising to me to read the documentation and then realize that the "redirect" setting didn't apply to page-load or hashchange cases.

What version of spine are you using? The route module was reworked a bit in 1.3.1

I'm using 1.3.1. You can see in route.coffee that the redirect option is only referenced in @Navigate, which is not called from @change.

I recently was looking at the Route module. The redirect option is expected to be a boolean or a function. I'm not sure what this feature is for exactly. I didn't see any reference to it in the docs.

If you are using Spine.Stack to setup your app you could set your default controller to handle unmatched routes on load.

The redirect option is only reachable if trigger is true and no routes are matched from within Route.navigate:

    if options.trigger
      @trigger('navigate', @path)
      routes = @matchRoutes(@path, options)
      unless routes.length
        if typeof options.redirect is 'function'
          return options.redirect.apply this, [@path, options]
        else
          if options.redirect is true
            @redirect(@path)

https://github.com/spine/spine/blob/dev/src/route.coffee#L94

Route.redirect is implemented as:

  @redirect: (path) ->
    window.location = path

https://github.com/spine/spine/blob/dev/src/route.coffee#L160

This would just do a hard refresh of the page. Presumably this is useful if you want to have the path requested make a request to the server instead of being handled by Spine.

I didn't know about the redirect option when I was setting up my app. In my app.js I setup an event listener for clicking links that allows the default action to happen or calls Route.navigate. You can see my gist here: https://gist.github.com/richard-flosi/10297918#file-app-js-L26 I'm using `target="_self"`` to do the equivalent of what redirect does by default. I'm using this for routes that I need to reach back out to the server for.

What do you want to happen when a route is not matched?

@richard-flosi I want it to be consistent with the docs, which say:

If you want unmatched routes to do something you can pass a redirect option to either have the window navigate to the unmatched route or perform some callback

http://spinejs.com/docs/routing

Thanks! I couldn't find those docs. redirect is not mentioned in the full api docs: http://spinejs.com/api/routing

You're right. That needs to be updated as well!

If memory serves we added the redirect option as it is more for dealing with cases where there was content that you didn't want in your normal js or html files. Using it this way you could bring into the spine app in a way that is traceable via the history/url. I think this could be especially useful for views or html content, but now that I think about it is not terribly useful if that path doesn't actually work if it is what is given as a url in the first place. So it sounds like it could use some work, as well as some documentation improvements.

I also see the potentially for an infinite loop issue should a redirect lead back to the spine app which leads to a redirect...

I have been incrementally updating my legacy application to a spine application, but I don't have the luxury of doing it all at once. As each module of my app is spinified, I don't want to go back through every module and change the routing logic.

Therefore, it's important for me to be able to consistently have controllers call @Navigate without worrying about whether the target URL has been converted to spine yet or not. Controllers calling @Navigate shouldn't have any knowledge of how the target action is implemented, anyway.

For this to work, Spine.Route should support a way for unmatched routes to fall through to the browser's native page loading mechanism, a la window.location=route.

That may not be the desired behavior for everyone, but for incremental development like I have to do, it's critical.

@jcarlson The way I setup my dev environment is to throw nginx (or apache) in front of it and then setup a layer of routing there as some paths need to go to Rails vs. Spine. Actually at the moment I have our soon to be legacy app (Backbone) and our refactor app (Spine) being served up by the same nginx depending on the subdomain name.

Is the problem you are having related your dev environment or all environments?

I guess I just don't understand how window.location=route is going to work for you b/c if the current route is leading you to the Spine app to begin with, won't it lead you there again?

Imagine my app has CRUD for Books and CRUD for Authors. When you visit the "show author" page, there is a nested list of books by the current author, and each title is a link to the "show book" page.

Today, my Books module is NOT Spinified, but the URL to show a book is "/books/:id". In my Author module, which is Spinified, the list of current author's books should be able to link to "/books/123" without having knowledge of whether the target is a Spine route or a plain-old-page-load.

To accomplish this, all navigation will go through my Spine Router. When the book title link is clicked, the controller prevents the default browser behavior and calls Spine.Route. If the Spine router can match the given route, then the appropriate controller in the stack takes over (such as moving from "Author Index" to "Show Author"). But if the router cannot match the route, I just set window.location=theRoute.

In the latter case, it causes a regular page load in the browser, which is the appropriate action, since my Book module is still implemented as server-side page rendering.

So in this case, no, the unmatched route won't get me back to my Spine app, because my server will respond with a different HTML page when I set window.location='/books/123'.

Also, I'm not having any problem, I was just offering an example of how the redirect functionality is in use in a real-world application.