evancz / url-parser

Parse URLs into nicely structured data
http://package.elm-lang.org/packages/evancz/url-parser/latest/
BSD 3-Clause "New" or "Revised" License
114 stars 29 forks source link

Get Params Without Parsing Url #39

Closed ghivert closed 7 years ago

ghivert commented 7 years ago

Hi.

I'm writing an application where we need to access common queryParams for every page. Right now, the package don't allow to parse separately url and queryParams. I'm forced to use a hack:

type Route
  = Films
  | Film Int
  | Vehicles
  | Vehicle Int
  | Settings
  | NotFound

type alias RouteParams =
  { route : Route
  , params : Params
  }

parseRoute : Parser (Route -> a) a
parseRoute =
  UrlParser.oneOf
    [ map Films top
    , map Film (s "films" </> int)
    , map Films (s "films")
    , map Vehicle (s "vehicles" </> int)
    , map Vehicles (s "vehicles")
    , map Settings (s "settings")
    ]

parseParams : Parser (Params -> a) a
parseParams =
  map Params <|
    top
      <?> UrlParser.intParam "page"
      <?> UrlParser.stringParam "color"

parseRouteParams : Parser (RouteParams -> a) a
parseRouteParams =
  map RouteParams <|
    parseRoute </> parseParams

parseLocation : Location -> RouteParams
parseLocation location =
  case (UrlParser.parsePath parseRouteParams location) of
    Just route ->
      route
    Nothing ->
      { route = NotFound
      , params = Params Nothing Nothing
      }

It works perfectly well, but not with NotFound page. If I'm typing address.com/foo?page=3, I can't get the queryParams. (And it's perfectly logic, as the path /foo does not exists, and top can not match it.)

Is there a way to get it working anyway?

Edit: After a quick search, I could use RouteParser.QueryString, but tbh, I find that the opinion of UrlParser generating a record much better than a Dict.

process-bot commented 7 years ago

Thanks for the issue! Make sure it satisfies this checklist. My human colleagues will appreciate it!

Here is what to expect next, and if anyone wants to comment, keep these things in mind.

evancz commented 7 years ago

I do not see why you think this is a hack. To me it just looks like composing parsers. If you write it this way, I'm not sure how much clearer it could get with a different design:

type alias RouteParams = { route : Route, params : Params }
type alias Params = { page : Maybe Int, color : Maybe String }

parser : Parser (RouteParams -> a) a
parser =
  let
    route =
      oneOf
        [ map Films top
        , map Film (s "films" </> int)
        , map Films (s "films")
        , map Vehicle (s "vehicles" </> int)
        , map Vehicles (s "vehicles")
        , map Settings (s "settings")
        ]

    params =
      map Params <|
        top
          <?> UrlParser.intParam "page"
          <?> UrlParser.stringParam "color"
  in
    map RouteParams (route </> params)

So I don't really see an alternate design that will do what you want. If you have a specific idea, please share it in another issue.

evancz commented 7 years ago

Ah, I guess the root question is actually about how to get info even on a NotFound address.

This is tracked in #40.