bidi icon indicating copy to clipboard operation
bidi copied to clipboard

`just-path` removes #

Open NOBLES5E opened this issue 7 years ago • 9 comments

Since just-path removes # so that a path /#/about-me becomes /, which makes the following code not working

(def bidi-route
  ["/" {"#/about-me"     :about-me
        "#/publications" :publications
        "#/teaching"     :teaching}])

(bidi/path-for bidi-route :publications)  ; => /#/publications

(bidi/match-route bidi-route "/#/publications") ; => nil

NOBLES5E avatar May 23 '18 06:05 NOBLES5E

I would suggest match-route function assumes the path provided is just the path we want to match without modification. If we want to remove the # or query parameters etc we should call just-path function by ourselves.

NOBLES5E avatar May 23 '18 06:05 NOBLES5E

As a "workaround" (not meaning to say this is a bug in bidi because on the server side this well-defined behavior) - when using accountant, you can just

(defn- unprefixed
  [path]
  (if (clojure.string/starts-with? path "/#")
    (subs path 2)
    path))

and then, e.g.,

  (accountant/configure-navigation!
   {:nav-handler (comp (fn [path] 
     ;; here goes your old nav handler
     ) unprefixed)
    :path-exists? (comp (fn [path] 
     ;; here goes your old exists handler
     ) unprefixed})

You must then rewrite the routes without the leading /#

dmichulke avatar Jul 02 '18 08:07 dmichulke

Depending on what you're trying to achieve, there's different solutions here. js/location.hash gives the result "#/about-me", meaning that the solution is to do (subs js/location.hash 1) for the URL.

However, if you have intentions of doing URLs like /about#/me as well as /#/foo, then doing this won't let you distinguish between the two. In this case you will need a scheme for distinguishing between them, I suppose you could drop the # in most cases, unless you had both /about#/me and /#/about/me and you wanted them to be different.

The intention of just-path was to prevent tripping over URLs with query params & other transient artifacts, particularly with ring. The correct behavior here is quite hard to get right. Ultimately bidi isn't intending to work on location hashes, but the ideal is to work on pure paths.

One could argue that if a location hash is found in a URL then use it. But that falls down because:

  • Location hashes can be used for in-page navigation (linking within elements on a page)
  • Users could break routing by adding a location hash to the URL

SevereOverfl0w avatar Jul 02 '18 12:07 SevereOverfl0w

@SevereOverfl0w

Yes, of course. I was considering only empty paths before that hash. But the general solution is to use js/location.hash (didn't know this one) which returns everything beginning with the hash.

As stated above, I agree you shouldn't change the bidi behavior, just maybe mention how it can be nicely resolved in a SPA. I posted the workaround because it's probably a usual problem people programming SPAs will have yet it wasn't immediately obvious (at least to me on a Monday after lunch) how to resolve it.

dmichulke avatar Jul 02 '18 13:07 dmichulke

Documentation may well be the best way to resolve this. We're having a focus on documentation at JUXT right now, so bidi may be a good candidate for improving as a result of that.

SevereOverfl0w avatar Jul 02 '18 13:07 SevereOverfl0w

Personally I'm a little puzzled by this, wouldn't a url fragment caused by an #section-name also be preserved on the server side? Or does that already occur?

Anyway, what is the expected way to resolve this?

Folcon avatar Mar 25 '20 20:03 Folcon

Url fragments aren't sent to the server, ever.

SevereOverfl0w avatar Mar 26 '20 11:03 SevereOverfl0w

Yea, my mistake, I've seen enough url fragment laden urls get routed that it never occurred to me to check if the browser was actually forwarding them...

Looking at the js/location.hash approach, not certain how to make this work, here's my example:

(def routes ["/"
             {"" :home
              "#/login"  :login
              "#/logout" :logout
              true :not-found}])

So I route to :login

(bidi/path-for routes :login)
#_=> "/#/login"

window.location.hash gives me "#/login" which when I check for matches gives me:

(bidi/match-route routes "#/login")
#_=> nil
(bidi/match-route routes "/login")
#_=> {:handler :not-found}
(bidi/match-route routes "/#/login")
#_=> {:handler :home}

So not quite clear how that works...

Folcon avatar Mar 26 '20 12:03 Folcon

BTW, this issue also trapped me.

HaskellZhangSong avatar Mar 19 '22 13:03 HaskellZhangSong