From d4c28211824844b9e3f3320da86d4b71227f155e Mon Sep 17 00:00:00 2001 From: there# <84727708+ForgottenGensym@users.noreply.github.com> Date: Sun, 30 May 2021 13:48:19 -0400 Subject: [PATCH 1/3] fix typo "Uknown URL" in Example.elm --- examples/Example.elm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Example.elm b/examples/Example.elm index befe70f..b83884f 100644 --- a/examples/Example.elm +++ b/examples/Example.elm @@ -126,7 +126,7 @@ viewRoute : Maybe Route -> Html msg viewRoute maybeRoute = case maybeRoute of Nothing -> - li [] [ code [] [ text "Uknown URL" ] ] + li [] [ code [] [ text "Unknown URL" ] ] Just route -> li [] [ code [] [ text (Debug.toString route) ] ] From 083bd088ad986d3a618e721e478a1398cbe21ef8 Mon Sep 17 00:00:00 2001 From: there# <84727708+ForgottenGensym@users.noreply.github.com> Date: Sun, 30 May 2021 15:07:44 -0400 Subject: [PATCH 2/3] use custom routeToString instead of Debug.toString --- examples/Example.elm | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/examples/Example.elm b/examples/Example.elm index b83884f..86cc40c 100644 --- a/examples/Example.elm +++ b/examples/Example.elm @@ -52,7 +52,6 @@ type Route | BlogList (Maybe String) | BlogPost Int - routeParser : Parser (Route -> a) a routeParser = P.oneOf @@ -61,6 +60,22 @@ routeParser = , P.map BlogPost (s "blog" P.int) ] +-- You could use Debug.toString here, but that is not recommended for production use +routeToString : Route -> String +routeToString route = + case route of + BlogList (Just name) -> + "BlogList (Just " ++ name ++ ")" + + BlogList Nothing -> + "BlogList Nothing" + + BlogPost num -> + "BlogPost " ++ String.fromInt num + + Home -> + "Home" + -- UPDATE @@ -129,4 +144,4 @@ viewRoute maybeRoute = li [] [ code [] [ text "Unknown URL" ] ] Just route -> - li [] [ code [] [ text (Debug.toString route) ] ] + li [] [ code [] [ text (routeToString route) ] ] From 41ce2711f9b1238d7a7034ba0501065091bcf41b Mon Sep 17 00:00:00 2001 From: there# <84727708+ForgottenGensym@users.noreply.github.com> Date: Sun, 30 May 2021 18:12:17 -0400 Subject: [PATCH 3/3] Comment the parts that are specific to elm/url --- examples/Example.elm | 98 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 17 deletions(-) diff --git a/examples/Example.elm b/examples/Example.elm index 86cc40c..c1d83c3 100644 --- a/examples/Example.elm +++ b/examples/Example.elm @@ -21,8 +21,11 @@ main = , view = view , update = update , subscriptions = subscriptions - , onUrlRequest = UrlRequest - , onUrlChange = UrlChange + , onUrlRequest = UrlRequest -- This should be a function of type `Browser.UrlRequest -> Msg`. + -- It isn't a handler like in JS, but a message that `update` can + -- process. + , onUrlChange = UrlChange -- Similarly, this is also a function that returns a message. However, + -- it accepts a `Url`, not a `Browser.UrlRequest`. } @@ -31,11 +34,15 @@ main = type alias Model = - { history : List (Maybe Route) + { history : List (Maybe Route) -- Keep track of the routes. The Maybe is because parsing can (and will) fail. , key : Nav.Key } +-- Nav.Key is needed for controlling navigation, it is provided by `init` and we store it in the `Model` +-- If you want to understand what its actually doing, the docs for it are linked below: +-- + init : () -> Url -> Nav.Key -> ( Model, Cmd Msg ) init _ url key = ( Model [ P.parse routeParser url ] key @@ -47,20 +54,68 @@ init _ url key = -- URL PARSING -type Route - = Home - | BlogList (Maybe String) - | BlogPost Int +type Route -- A custom type to describe a route. This is how our parser will give us its results. + = Home -- "/" + | BlogList (Maybe String) -- "/blog?search=..." (`Just ...`) or "/blog" (`Nothing`) + | BlogPost Int -- "/blog/" +-- A parser is built by starting with simple builtin parsers, like "the root path" or "this directory", +-- and then these parsers are combined with *parser combinators*, like and , which are explained +-- more concretely and in greater detail below. routeParser : Parser (Route -> a) a routeParser = - P.oneOf - [ P.map Home top - , P.map BlogList (s "blog" Q.string "search") - , P.map BlogPost (s "blog" P.int) + P.oneOf -- This is the first parser combinator, it takes a list + -- of parsers, and if one of the parsers fails, it just + -- tries the next one given. The URL parser it returns + -- can go through all of the different patterns specified + -- and generate the correct representation of the URL. + + [ P.map Home top -- `top : Parser a a` and `s : String -> Parser a a` + -- are the two main "starting points" for building a + -- basic parser. top represents the root page "/", + -- and s is used to represent a particular directory. + + -- Just like Maybe.map allows you to have a function + -- run in the context of the Maybe, P.map allows us to + -- insert a function "into" the parser in a similar manner. + -- Here, we are using the type constructor `Home` to turn + -- basic `top` parser into one that, when given a matching + -- URL ("/" in this case), will return Home. + + , P.map BlogList (s "blog" Q.string "search") -- The parser combinator is used to combine a + -- standard URL parser (Parser ...) with a + -- query string parser (Q.Parser ...). + + -- Here, the `Q.string : String -> Q.Parser (Maybe String)` + -- function is used as a basic query string parser. + -- `Q.String` will parse the given query parameter, so this + -- is going to extract the query parameter "search". + -- Note that when mapping over a parser created with , + -- the the value passed into the mapping function (here + -- `BlogList`), will be the result from the query parser. + -- Since `Q.String "..."` is a `Q.Parser (Maybe String)`, + -- the mapping function should take a `Maybe String`. + + , P.map BlogPost (s "blog" P.int) -- Simply converting the directory name to an int + -- wouldn't always work, and we would have to handle the + -- Maybe ourselves. Instead of having to deal with a + -- `BlogPost (Maybe Int)`, or having to get the parser to + -- fail on non-integers ourselves, we can use the builtin + -- P.int do handle the directory name as an integer. + -- Note that this is in contrast to the standard query + -- parser, where the parser returns a `Maybe String`. + -- There, not finding a particular query parameter is not + -- indicative of failure, but just is something should be + -- dealt with as an optional value. + + -- The parser combinator is used just like , + -- but is used to combine two URL parsers, where + -- instead of the second parsing query parameters after + -- the first, it will parse subdirectories found after + -- the first. ] --- You could use Debug.toString here, but that is not recommended for production use +-- You could use `Debug.toString : a -> String` here, but that is not recommended for production use routeToString : Route -> String routeToString route = case route of @@ -90,20 +145,29 @@ update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of UrlChange url -> - ( { model | history = P.parse routeParser url :: model.history } + ( { model | history = P.parse routeParser url :: model.history } -- The main part of this is, + -- `P.parse routeParser url`, + -- where we are parsing the URL + -- with the parser we just built. + -- Then, we use the `::` operator + -- to add it to the history list. , Cmd.none ) UrlRequest request -> - case request of + case request of -- onUrlRequest doesn't actually provide a Url, but a Browser.UrlRequest. + -- It isn't just a URL, it needs to carry some additional information. + -- It is explained on the docs for Browser.UrlRequest, here: + -- https://package.elm-lang.org/packages/elm/browser/latest/Browser#UrlRequest Browser.Internal url -> ( model - , Nav.pushUrl model.key (Url.toString url) + , Nav.pushUrl model.key (Url.toString url) -- Change the URL, but do not trigger a page load. + -- Adds a new entry to the browser history. ) Browser.External url -> ( model - , Nav.load url + , Nav.load url -- Basically a redirect (always a page load) ) @@ -139,7 +203,7 @@ viewLink url = viewRoute : Maybe Route -> Html msg viewRoute maybeRoute = - case maybeRoute of + case maybeRoute of -- URL parsing can succeed or fail, we must handle both cases. Nothing -> li [] [ code [] [ text "Unknown URL" ] ]