`just-path` removes #
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
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.
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 /#
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
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.
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.
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?
Url fragments aren't sent to the server, ever.
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...
BTW, this issue also trapped me.