etaque/elm-route-parser

A friendly route parser based on parser combinators


Install
elm-package install etaque/elm-route-parser 3.0.0

Documentation

Elm Route Parser

elm package install etaque/elm-route-parser

A typed route parser in Elm, with a nice DSL built on top of parser combinators. Designed to work well with path or hash signals from elm-history: just map an action on it and do a RouteParser.match to update your current route, then use this route to render the right view.

See elm-transit-router for a full featured SPA router compatible with this package.

Under the hood, it's just a list of matchers String -> Maybe Route, and the first match wins. For that, there is a DSL tailored to mimic path shapes, ensuring typesafety with the power of parser combinators without the surface complexity:

"/some/" int "/path"

If the dynamic param isn't parsable as an int, it won't match as an acceptable path for this route:

"/some/1/path" -- match!
"/some/wrong/path" -- no match

Note that you can create and use custom param parsers, and custom matchers.

Usage

DSL

Example:

import RouteParser exposing (..)

type Route
  = Home
  | Foo String
  | Bar Int
  | Baz Int String Int

matchers : List (Matcher Route)
matchers =
  [ static Home "/"
  , dyn1 Foo "/foo/" string ""
  , dyn2 (\id slug -> Bar id) "/bar/" int "-" string ""
  , dyn3 Baz "/baz/" int "/a/" string "/b/" int "/c"
  ]

-- static
match matchers "/" == Just Home

-- dyn1
match matchers "/foo/foo" == Just (Foo "foo")

-- dyn2
match matchers "/bar/12-some-slug" == Just (Bar 12)
match matchers "/bar/hey-some-slug" == Nothing

-- dyn3
match matchers "/baz/1/a/2/b/3/c" == Just (Baz 1 "2" 3)

Custom matchers

If the DSL isn't enough for your needs, you can also use one of those escape hatches to build a custom matcher:

  • customParam to build a param extractor from a Parser instance, so it can be used with the DSL ;
  • parserMatcher, takes a Parser Route instance ;
  • rawMatcher, takes a String -> Maybe Route function.

See elm-combine for more information on parsers.

Route matching delegation

Use mapMatchers to delegate a bunch of routes to a component:

    -- global routing:

    type Route = Home | Admin AdminRoute

    matchers =
      [ static Home "/" ] ++ (mapMatchers Admin adminMatchers)

    -- can be delegated to a component without knowdedge of global routing:

    type AdminRoute = Dashboard | Users

    adminMatchers =
      [ static Dashboard "/admin", static Users "/admin/users" ]

Reverse routing

The reverse router has yet to be written manually:

toPath : Route -> String
toPath route =
  case route of
    Home -> "/"
    Foo s -> "/foo/" ++ s
    Bar i -> "/bar/" ++ (toString i)
    Baz i s j -> "/baz/" ++ (toString i) ++ "/a/" ++ s ++ (toString j) ++ "/c"

Glad to take any PR on that part, there is room for improvement.

Query string

A query string parser is also available under RouteParser.QueryString module:

import RouteParser.QueryString as QueryString

queryStringPart : String
queryStringPart =
  "?foo=1&bar=baz&bar=taz"

params : String -> Dict String (List String)
params = 
  QueryString.parse queryStringPart

-- Dict.get "foo" params == Just [ "1" ]
-- Dict.get "bar" params == Just [ "baz", "taz" ]

Credits