type alias AppUrl =
{ path : List String
, queryParameters : QueryParameters
, fragment : Maybe String
}
type alias QueryParameters =
Dict String (List String)
Which works really nicely with pattern matching!
import AppUrl exposing (AppUrl)
import Dict
parse : AppUrl -> Maybe Page
parse url =
case url.path of
[] ->
Just Home
[ "product", productId ] ->
String.toInt productId |> Maybe.map (Product << ProductId)
[ "products" ] ->
Just
(ListProducts
{ color = Dict.get "color" url.queryParameters |> Maybe.andThen List.head
, size = Dict.get "size" url.queryParameters |> Maybe.andThen List.head
}
)
_ ->
Nothing
type Page
= Home
| Product ProductId
| ListProducts Filters
type ProductId
= ProductId Int
type alias Filters =
{ color : Maybe String
, size : Maybe String
}
Some design stuff:
Parse: Avoid the complex types Url.Parser has, and use simple list pattern matching instead. Decode away all percentage escapes so you never need to think about them.
Stringify: No separate API like Url.Builder. Escape the minimum needed with percentage escapes.
Keep it simple: URLs shouldn’t be that complicated. List pattern matching, Dict and Maybe is the bread and butter of AppUrl, rather than parsers, tricky type annotations and the </> and <?> operators.
This is very nice and sane! I ran into a corner case a couple of times when trying to parse only the query params, which would fail when there is a non-root path. This makes much more intuitive sense!
I’ve always found it a bit odd that elm/url has this Parser type with the potential of storing lots of metadata, but the only way of running it is:
parse : Parser (a -> a) a -> Url -> Maybe a
So on parse error, you only get Nothing.
On the other hand, I’ve never had the need for parse errors anyway. Either a URL matches, or it does not, and it’s usually easy to figure out why. The only reasonable UX is to show “not found”.
So having an AppUrl -> Maybe a function is no worse.
I love this! In my mind routing should always have been a simple pattern match
I don’t know if you drew inspiration from the way ReasonReact does it, but very nicely done!
Nice library, the only problem I have with it is with that query params being List inside the Dict. It makes things for inserting and retrieving one parameter (which is like 99,99% of cases) unnecessarily complex to my taste.
We are also having separate Routers, one for each page/subpage, and we are compounding them together from the higher routers, so we need the way for joining resulting Url structure together, which again is a pain with those Dict of Lists. In the end we plan to wrap AppUrl to our own module and provide those functions ourselves.
But apart from that, I really like how the parsing of the Url is now just a pattern matching.