Skip to content

Commit

Permalink
Making Msg a Custom Type
Browse files Browse the repository at this point in the history
As we have more than one possible `Msg` now, we have to account for various scenarios ...

1. Create a proper `Msg` type (not an alias)
2. Convert the `update` function into a better `case` expression to handle two of our `Msg` types
  • Loading branch information
badlydrawnrob committed Jan 26, 2024
1 parent 22ff9fd commit 3e66e8a
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 13 deletions.
88 changes: 88 additions & 0 deletions elm-in-action/03/notes/Notes.elm
Original file line number Diff line number Diff line change
Expand Up @@ -420,3 +420,91 @@ anotherCustomTypeVariant index =
age
Nothing ->
0


-- 3.2.4 -----------------------------------------------------------------------

-- Flexible messages with custom types --
--
-- We don't want to have one `{ record = "data" }` for lots of different `Msg`
-- as each `onClick` serves it's own purposes. This is a good case for a
-- separation of concerns.

-- Current code --
--
-- 1. Our original `Msg` is a simple record
-- 2. A case statement with a new (and related) `Msg`
-- - this hasn't been fully integrated yet.
-- - falls back to the current model.

-- Let's just run through this quickly:
-- 1. `update` function (via `main`) gets passed our `onClick` message
-- 2. Our `viewThumbnail` function creates this message
-- 3. `update` gets passed the `Msg` (type alias) and the `Model` (type alias)
-- 4. `onClick` sends a `ClickedPhoto` `description`
-- 5. The `model` variable accesses `initialModel` ...
-- 6. `selectedUrl` in `initalModel` is changed to match `msg.data`
-- `{ description = "ClickedPhoto", data = "1.jpeg" }`

type alias Msg =
{ description : String, data : String }

update : Msg -> Model -> Model
update msg model =
case msg.description of
"ClickedPhoto" ->
{ model | selectedUrl = msg.data }
"ClickedSurpriseMe" ->
{ model | selectedUrl = "2.jpeg" }
_ ->
model

-- We also have --
--
-- : #1 A `ThumbnailSize` type
-- : #2 Two `onClick` functions
-- : #3 Two related `msg.descriptions`:
-- - "ClickedPhoto"
-- - "ClickedSurpriseMe"
-- : #4 An extra `Msg` trigger:
-- - A radio button to select from


-- Changing things up ----------------------------------------------------------

-- 1. Replace our type alias Msg declaration with a type Msg declaration.
-- 2. Revise update to use a case-expression that destructures our new custom type.
-- 3. Change our onClick handler to pass a type variant instead of a record.

type Msg -- Replaces earlier type alias
= ClickedPhoto String -- Replaces `ClickedPhoto` message
| ClickedSize ThumbnailSize -- NEW clicked a thumbnail size
| ClickedSurpriseMe -- Replaces `ClickedSurpriseMe` message


update : Msg -> Model -> Model
update msg model =
case msg of
ClickedPhoto url ->
{ model | selectedUrl = url } -- Previously `msg.description == "ClickedPhoto"
ClickedSurpriseMe ->
{ model | selectedUrl = "2.jpeg" } -- Nothing to destructure for this


-- This change means our onClick handlers now expect type variants instead of records.
-- We’ll need to make this change in view:

-- Old: onClick { description = "ClickedSurpriseMe", data = "" }
-- New: onClick ClickedSurpriseMe

-- Let's also make this change in `viewThumbnail`:

-- Old: onClick { description = "ClickedPhoto", data = thumb.url }
-- New: onClick (ClickedPhoto thumb.url)


-- Compiler errors -------------------------------------------------------------

-- Our type Msg has three possibilities.
-- This requires 3 branches to resolve.
-- ..........
26 changes: 13 additions & 13 deletions elm-in-action/03/src/PhotoGroove.elm
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ import Array exposing (Array)


-- View ------------------------------------------------------------------------
-- : #1 Specifying our `onClick` handler message.
-- - Our message is a record
-- : #1 Specifying our `onClick` handler message(s)
-- - Our message is a Custom Type
-- - `Html`'s type variable reflects the type of message
-- it sends to `update` in response to event handlers.
-- Our event handler here is `onClick`!
-- Our event handler(s) here are `onClick`!
--
-- : #2 Add another message (a record) if a button is clicked
--
Expand All @@ -41,15 +41,17 @@ import Array exposing (Array)
-- radio button the user chooses. The `sizeToString` function converts
-- a `ThumbnailSize` type to a `"string"` which we add to the `class`.

type alias Msg =
{ description : String, data : String } -- #1
type Msg = -- #1
= ClickedPhoto String
| ClickedSize ThumbnailSize
| ClickedSurpriseMe

view : Model -> Html Msg -- #1
view model =
div [ class "content" ]
[ h1 [] [ text "Photo Groove" ]
, button
[ onClick { description = "ClickedSurpriseMe", data = "" } ] -- #2
[ onClick ClickedSurpriseMe ] -- #2
[ text "Surprise Me!" ]
, h3 [] [ text "Thumbnail Size:" ]
, div [ id "choose-size" ]
Expand All @@ -75,7 +77,7 @@ viewThumbnail : String -> Photo -> Html Msg
viewThumbnail selectedUrl thumb =
img [ src (urlPrefix ++ thumb.url)
, classList [ ("selected", selectedUrl == thumb.url) ]
, onClick { description = "ClickedPhoto", data = thumb.url }
, onClick (ClickedPhoto thumb.url)
] []

viewSizeChooser : ThumbnailSize -> Html Msg -- #3
Expand Down Expand Up @@ -146,13 +148,11 @@ getPhotoUrl index =

update : Msg -> Model -> Model
update msg model =
case msg.description of
"ClickedPhoto" ->
{ model | selectedUrl = msg.data }
"ClickedSurpriseMe" ->
case msg of
ClickedPhoto url ->
{ model | selectedUrl = url }
ClickedSurpriseMe ->
{ model | selectedUrl = "2.jpeg" }
_ ->
model


-- Main ------------------------------------------------------------------------
Expand Down
9 changes: 9 additions & 0 deletions elm-in-action/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ Looking back at some of the [Racket lang challenges](https://github.com/badlydra
- http://tinyurl.com/elm-lang-maybe-dont-overuse
- Give an example of _deconstructing_ `Just` and `Nothing` (see line `381` in `Notes.elm`)
- https://exercism.org/tracks/elm/concepts/maybe#
22. See another version of `onClick` here with a button.
- https://guide.elm-lang.org/architecture/buttons
23. Migrating from a single `Msg` (a record) to multiple, and different kinds of `Msg` data.
- Cut out **everything** except for the messages and the `Msg` type alias.
- Note the problems with trying to smush it all into a single `Msg` (changing the data in the `Msg` type alias creates an error, adding unrelated data to unrelated `Msg` types with `onClick` feels wrong, adding a new type of message — one is a `"string"` the other is a `ThumbnailSize`, etc.)
- **Seperation of concerns!**
- Converting `Msg` as a CUSTOM TYPE
- Line `440` gives a so-so summary of what was happening in our original code (how `Msg` was getting passed around).
- **BETTER TO MAKE A DIAGRAM OF ALL OF THIS!!!**


[^1]: I have color-coordinated some sections with highlights on the _Elm in Action_ ebook
Expand Down

0 comments on commit 3e66e8a

Please sign in to comment.