🌳
Walking though the Elm woods
  • Introduction
  • Structure of the book
  • Frequently asked questions
    • How can different types share data?
    • How to break Dependency Cycles?
    • How to structure an Elm project?
    • How to turn a Msg into a Cmd Msg?
    • How to update nested Records?
    • What are comparable types?
    • Why are Booleans bad?
    • 🔜Future topics
  • Recipes
    • Writing a Single Page Application
      • Share state across pages
      • Debounced Validation
      • Reusable views
    • Making impossible states Impossible
      • Non empty lists using Zippers
      • Restrict records using Opaque Types
      • Write safer functions using Phantom Types
    • Designing Elm package APIs
      • Create upwards compatible APIs
    • 🔜Future topics
  • Frameworks and packages
    • elm/parser
    • mdgriffith/elm-ui
    • 🔜Future topics
Powered by GitBook
On this page
  • Question
  • Answer
  • Further reading

Was this helpful?

  1. Recipes
  2. Writing a Single Page Application

Reusable views

Main.elm
type alias ButtonModel =
    { text : String
    }

type alias Model =
    { button: ButtonModel
    }

type ButtonMsg =
    ButtonPressed

type Msg =
    ButtonSpecific ButtonMsg
    | Reset

update : Msg -> Model -> (Model,Cmd Msg)
update msg ({button} as model) =
    case msg of
        ButtonSpecific ButtonPressed -> 
            ( { model
              | button = 
                  { button
                  | text = "Thanks!"
                  }
              }
            , Cmd.none
            ) 
        _ ->
            (model, Cmd.none)

{-|
  This can be moved into a seperate file as it still 
  depends on Msg.
-}
myButton : ButtonModel -> List (Attribute Msg) -> List (Html Msg) -> Html Msg
myButton {text} attributes children =
    Html.div []
        (Html.button
            [ (Events.onClick <| ButtonSpecific ButtonPressed)
              :: attributes 
            ]
            [ Html.text text
            ]
        )
        :: children

view : Model -> Html Msg
view model =
    Html.div []
        [ Html.h1 []
            [ Html.text "Here is a reusable view for a button"
            ]
        , myButton []
            [ Html.text "Click me!"
            ]
        ]
Main.elm
import MyButton

type alias Model =
    { button: MyButton.Model
    }

type Msg =
    | ButtonPressed String
    | Reset

update : Msg -> Model -> (Model,Cmd Msg)
update msg ({button} as model) =
    case msg of
        ButtonPressed string -> 
            ( button |> MyButton.buttonPressed string
            , cmd
            ) 
        _ ->
            (model, Cmd.none)

view : Model -> Html Msg
view ({button} as model) =
    Html.div []
        [ Html.h1 []
            [ Html.text "Here is a reusable view for a button"
            ]
        , MyButton.view ButtonPressed
            button
            []
            [ Html.text "Click me!"
            ]
        ]
MyButton.elm
type alias Model =
    { text : String
    }

buttonPressed : String -> Model -> Model
buttonPressed string button =
  { button
  | text = string
  }

view : (string -> msg) -> ButtonModel -> List (Attribute msg) -> List (Html msg) -> Html msg
view onClick {text} attributes children =
    Html.div []
        (Html.button
            [ (Events.onClick <| onClick <| "Thanks!")
              :: attributes 
            ]
            [ Html.text text
            ]
        )
        :: children

Question

How can I build a reusable view function with its own model?

Answer

The trick is to pass the Messages along.

Further reading

PreviousDebounced ValidationNextMaking impossible states Impossible

Last updated 5 years ago

Was this helpful?

📄Article:

📄Article: by Boolean Julian Jelfs

❗Example:

❗Example:

❗Example:

rofrol/elm-best-practices.md
Render props for Elm
NoRedInk/elm-sortable-table
abadi199/datetimepicker
ContaSystemer/elm-menu