78: Route Components - JSToElm
All Episodes

78: Route Components

Further down the rabbit hole we go with routes from pages to components. We avoid actually getting to the topic at hand pretty well, but eventually tackle it, get lost several times along the way. Come along and get totally turned around with us!

From Page to Component

  • Keeping our application modular, that means components

    • Not your React Components
    • Basically an Elm app that doesn’t expose a program
    • Does not have a ‘main’
    • Rather it exposes it’s model/init, update, and view functions for other Elm modules to use

      • Sound similar to life cycle methods?
    • The modules state will be stored in the main with a little ‘m’ model
    • expose Model, Msg, update, view
    • and init doesn’t take () unit any more. Remember it’s called unit!!!

      • Why doesn’t it? bc it’s not being invoked with the arguments that main would be, it’s exposing the other functions that it has. maybe?

Gettin’ it done

  1. Storing an instance of the component’s model in Main’s model? Maybe not so much. The effect would be growing fields in the model for each and every component. 😢
  2. A better solution would be to gather the state of those components in that particular page constructor. 👍
  3. Now main model will hold an instance of the component’s model when it’s part of the current Model.page instance… I think.
  4. In the function setNewPage where we are unwrapping our Maybe Route we can now use a let expression to destructor the particular component.init tuple ,

    1. That takes care of the model, for the cmd’s you’ll notice that you get a type error bc setNewPage returns (Model, Cmd Msg ) but now it’s (Model, Cmd "Commponent".Msg)
    2. The solution is to add msg that wrap other msg’s. This is a way to pass component messages from the update function through to the component msg.
    3. Don’t over look Cmd.map in order for this to work properly.
    4. applies a function to messages that commands produce, in this particular case passing AccountMsg into the update function to be unwrapped.
  5. Display and update the view

    1. whenever the component produces a message Html.map will wrap it with it’s message that main see’s as just a regular message,
    2. Your ‘loading’ isn’t updating bc we aren’t handling it..yet.
  6. Update to handle component’s message

    1. Adding model.page making the pattern match on the case statement not just msg, but also model.page for each branch.
    2. Here you can _ one of the tuple values to ignore that particular match. This is wickedly powerful!!!
    3. For a component’s msg and model you can pattern match that each value of the tuple is of type component msg and component model it will then only match if both the inner tuple values are of type from that component.
  7. It might be helpful to have a diagram of what is happening from Main -> Page -> Component TODO:
setNewPage : Maybe Routes.Route -> Model -> ( Model, Cmd Msg )
setNewPage maybeRoute model =
    case maybeRoute of
        Just Routes.Home ->
            let
                ( publicFeedModel, publicFeedCmd ) =
                    PublicFeed.init ()
            in
            ( { model | page = PublicFeed publicFeedModel }, Cmd.map PublicFeedMsg publicFeedCmd )

        Just Routes.Account ->
            let
                ( accountModel, accountCmd ) =
                    Account.init
            in
            ( { model | page = Account accountModel }, Cmd.map AccountMsg accountCmd )

        Nothing ->
            ( { model | page = NotFound }, Cmd.none )

Do it again!

  1. Practice, practice, practice. I call this the blunt approach. For me there is no ‘ah-ha’ moment. There is just, hopefully, a slow steady improvement. And the slow forgetfulness that I didn’t understand it at one time. Like a child touching the stove. After doing it a couple times, they realize not to touch it bc it’s hot, eventually the simply don’t touch a hot stove, because d’uh it’s hot.
  2. Import Component
  3. Add component model as a constructor to the page the component is on in the custom type.
  4. Add component Msg to main update function
  5. Branch for component Msg with destructure matching, on both the msg & model tuple. BUT then also on the updated model result from calling the components update function
  6. Subscriptions too!

Navigation Links 🤷‍♀️

  1. pushState JS method to manipulate the browsers url path, WITHOUT reloading the page
  2. pushUrl accepts Broswer.Navigation.Key and String path and returns a Cmd this command instructs Elm Architecture to call history.pushState on your app’s behalf.
  3. Some helper functions for Routes.elm

    1. routeToUrl & href : Route -> Html.Attribute msg
    2. These basically take the Route and return the desired string to then use in Elm’s Cmd for pushState
    3. The update function matches on Visit and returns the model, with a cmd Navigation.pushUrl model.navigationKey (Url.toString url)
    4. The helper function Routes.href is used in the viewHeader as basicly a ‘Link’ so you’re not constantly converting the Route to string across the application, but using Type Route to always use the same string for the new browser url.

Picks

Resources

Now sh

Programming Elm

ElmStatic

Cognitive biases

Follow

Published 25 Apr 2019

A show about learning Elm, Functional Programing, and generally leveling up as a JS developer.
JavaScript To Elm on Twitter