elm-lang/Navigation with hash and query string

816 views
Skip to first unread message

Charlie Koster

unread,
Dec 9, 2016, 8:53:14 PM12/9/16
to Elm Discuss
I submitted an issue to evancz/url-parser but it ended up being human error on my part. However, I'm making this post because of my closing comment on that issue.

To summarize, I'm using `UrlParser.parseHash` along with `Url.stringParam` and I can't get that parser to work. After some digging I noticed that elm-lang/Navigation is providing me a `Location` that doesn't have a search attribute. Here are two examples.

Navigate to http://localhost:8080/test?a=b&c=d
Logging the Location gives me:
{ href = "http://localhost:8080/test?a=b&c=d", host = "localhost:8080", hostname = "localhost", protocol = "http:", origin = "http://localhost:8080", port_ = "8080", pathname = "/test", search = "?a=b&c=d", hash = "", username = <internal structure>, password = <internal structure> }

Notice the search attribute as my query string.

Navigate to http://localhost:8080/#test?a=b&c=d
Logging the Location gives me:
{ href = "http://localhost:8080/#test?a=b&c=d", host = "localhost:8080", hostname = "localhost", protocol = "http:", origin = "http://localhost:8080", port_ = "8080", pathname = "/", search = "", hash = "#test?a=b&c=d", username = <internal structure>, password = <internal structure> }

Notice the search attribute is an empty string and the hash attribute contains both the hash and the query string

My question to the community.. Is this a bug with elm-lang/Navigation, evancz/url-parser, or not a bug at all because that's how hashes work and I have to deal with this manually?

Nick H

unread,
Dec 9, 2016, 9:06:55 PM12/9/16
to elm-d...@googlegroups.com
I would call this not a bug, since it conforms to the URL standard. in your second example, ?a=b&c=d is not a query string, but a part of the fragment string.

This is just one of several ways that URL syntax can lead you down a dark alley and steal your wallet.

--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Witold Szczerba

unread,
Dec 10, 2016, 5:28:51 PM12/10/16
to elm-d...@googlegroups.com
Nick is right. The two URLs:
have _nothing_ but the "origin" part in common. Note that everything after the `#` is just for the browser, the backend server will never get that. It won't tell the difference between:
and

Regards,
Witold Szczerba
Message has been deleted

Charlie Koster

unread,
Dec 10, 2016, 6:04:42 PM12/10/16
to Elm Discuss
Thanks for the responses.

As a follow up, I want to start using parsePath instead of parseHash so that I can start using the query string.

A problem I'm noticing is that if I have links in the app with the href attribute set to "/somePage" the page reloads which is undesirable with it being a Single Page App.

I have two ideas for solutions.
  1. Use onClick with a Msg instead of href
  2. Set preventDefault to True on all of the links in my application
The first option adds a bunch of extra Msgs I wouldn't otherwise need. The second option adds a lot of boilerplate and is prone to devs forgetting to preventDefault.

Is there a better option I'm not thinking of? I want to use parsePath so that evancz/url-parser can parse my query string parameters, but I don't want a lot of extra boilerplate to prevent the page from reloading when a user clicks a link.

Magnus Rundberget

unread,
Dec 11, 2016, 8:15:52 AM12/11/16
to Elm Discuss

Charlie Koster

unread,
Dec 11, 2016, 10:00:23 AM12/11/16
to Elm Discuss
Thanks for the response, Magnus.

If I'm understanding correctly, you have a parent div that prevents default click events of child elements, including your anchor tag, and turns them into `Attribute msg`.

Your solution is interesting, although it looks like a fair amount of boilerplate and work arounds and I'm trying to avoid both.

It might be worthwhile for me to dig through some of the popular JS frameworks routing source code to see how they solve this problem. AngularJS solves this with a custom href attribute called ui-sref. Maybe a custom attribute is the answer for Elm too?

Charlie Koster

unread,
Dec 11, 2016, 10:51:19 AM12/11/16
to Elm Discuss
I think I came up with a workable solution by adding a tiny bit of abstraction.

import Html exposing (Attribute, Html, a)
import Html.Events exposing (onWithOptions, defaultOptions)
import Model exposing (Msg(..))
import Json.Decode as Json


link : String -> List (Attribute Msg) -> List (Html Msg) -> Html Msg
link route attributes children =
    let
        clickHandler =
            onWithOptions "click" defaultOptions <| Json.succeed <| NavigateTo route

        attrs =
            clickHandler :: attributes
    in
        a
            attrs
            children

What this allows me to do is replace these:

a [ onClick (NavMsg1 param1), class "some class", title "Do something" ] [ text "Do something" ]
a [ onClick (NavMsg2), title "Do something else" ] [ text "Dom something else" ]
a [ onClick (NavMsg3 param2 param3), class "is-disabled", title "Delete thing" ] [ text "Delete thing" ]

..which has a lot of custom Msgs (one Msg per route).. and replace it with

link ("/something?param1=" ++ param1) [ class "some class", title "Do something" ] [ text "Do something" ]
link ("/home") [ title "Do something else" ] [ text "Dom something else" ]
link ("/deletePage?param2=" ++ param2 ++ "&param3=" ++ param3) [ class "is-disabled", title "Delete thing" ] [ text "Delete thing" ]

The above reads very similar to using an anchor tag with an href attribute which is what I was hoping for.

Additionally, just as with your solution, there is only one Msg for going to the different pages rather than a unique Msg for every different page.

Nicolas Artman

unread,
Dec 11, 2016, 10:51:20 AM12/11/16
to elm-d...@googlegroups.com
Standards *compliant. Sorry about the typo.
On Sat, Dec 10, 2016 at 12:08 PM Nicolas Artman <ni...@caffeine.tv> wrote:
Yep, it's standard complaint as Nick said. This behavior also agrees with the routing behavior for most JS client stacks I've used as well.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.

Nicolas Artman

unread,
Dec 11, 2016, 10:51:20 AM12/11/16
to elm-d...@googlegroups.com
Yep, it's standard complaint as Nick said. This behavior also agrees with the routing behavior for most JS client stacks I've used as well.
On Fri, Dec 9, 2016 at 6:06 PM Nick H <falling...@gmail.com> wrote:
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.

Witold Szczerba

unread,
Dec 11, 2016, 4:11:48 PM12/11/16
to elm-d...@googlegroups.com
Why is it so important that your links does not start with a `#` aka fragment part?

Regards,
Witold Szczerba

--

Charlie Koster

unread,
Dec 11, 2016, 4:35:45 PM12/11/16
to Elm Discuss
If I use hashes I now have to do manual work in order to use and parse query strings.

Let's say I want to handle a query string such as "myPage?search=things&order=asc". If there's a hash in front of "myPage" the query string doesn't get interpreted as a search string by elm-lang/Navigation (correct behavior), and evancz/url-parser doesn't parse my query string parameters. As a result, I'd have to parse the technically incorrect query string from the hash by hand.

I'd rather use a URL where the query string is technically correct in order to avoid future issues with these packages or other url and routing packages.

Witold Szczerba

unread,
Dec 11, 2016, 5:05:46 PM12/11/16
to elm-d...@googlegroups.com
There is nothing "technically" incorrect in e.g. 
<a href="myapp.com/#/account/15?sortBy=date">15</a>

All the SPA I have seen had URLs like this, because this is just the browser who cares about that part of the location. 

This is why, when creating e.g.:
<a href="/#/account/16?sortBy=date">next</a>
there is no need to hack browser to stop it from page reload as this is all about going somewhere else on the same "Page", the "P" in SPA.

All I wrote in Elm was a small app with two forms and I did not have to parse location, but in AngularJS the location services (or similar plugins) have no troubles parsing and destructuring the page fragment part. 

Looking at https://github.com/evancz/url-parser it seems that this library is covering only very basic parsing capabilities, there should be more complete libs in the public package repository though.

Regards,
Witold Szczerba

--

Charlie Koster

unread,
Dec 11, 2016, 5:23:58 PM12/11/16
to Elm Discuss
I don't disagree with anything your wrote there, but I still have this problem to deal with now. The path of least resistance happens to also be the path that uses query strings that are technically correct (nevermind about technically correct URLs).

If a library comes along and makes parsing hashes that contain semantic query strings easy, then I'll consider using that library. At the moment the solution I have now contains a very small workaround and has unblocked me from being able to use query string parameters.

Nick H

unread,
Dec 11, 2016, 6:16:03 PM12/11/16
to elm-d...@googlegroups.com
Maybe Bogdanp/elm-querystring could be helpful?

On Sun, Dec 11, 2016 at 2:23 PM, Charlie Koster <ckos...@gmail.com> wrote:
I don't disagree with anything your wrote there, but I still have this problem to deal with now. The path of least resistance happens to also be the path that uses query strings that are technically correct (nevermind about technically correct URLs).

If a library comes along and makes parsing hashes that contain semantic query strings easy, then I'll consider using that library. At the moment the solution I have now contains a very small workaround and has unblocked me from being able to use query string parameters.

--

Bob Hutchison

unread,
Dec 16, 2016, 12:47:16 PM12/16/16
to elm-d...@googlegroups.com
Hi,


Navigate to http://localhost:8080/#test?a=b&c=d


What happens if you write it according to (https://tools.ietf.org/html/rfc3986#section-4.1):

http://localhost:8080/?a=b&c=d#test

--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.

Nicolas Artman

unread,
Dec 16, 2016, 12:55:06 PM12/16/16
to elm-d...@googlegroups.com
I think you just need the equivalent of a custom component (like react-router's <Link />) or attribute (like angular UI-router's ui-sref) that uses html5 pushState for all the routing. That is the modern, standards-compliant way to do SPA routing. I would strongly caution anyone against using hash fragment based routing.

I'd be surprised to find out there isn't already a solution in the package repository, but if there isn't then it should still be easy to create a function that wraps the content in an <a> tag with a proper custom handler that sends a message and causes a history.pushState for all route transitions.
Reply all
Reply to author
Forward
0 new messages