Created
September 9, 2016 19:51
-
-
Save markhamburg/dd2b5b1d30db1f03ccee055a4e070677 to your computer and use it in GitHub Desktop.
An example of an Elm view with both shared and private state — in this case a counter with an integer shared state and a private stack of values.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Html exposing (..) | |
import Html.Attributes exposing (..) | |
import Html.App | |
import Html.Events exposing (..) | |
type CounterMsg | |
= Decrement | |
| Increment | |
| Push | |
| Pop | |
type CounterPrivate = | |
Private (List Int) | |
-- Initialize the private data for a counter | |
initCounterPrivate : Int -> CounterPrivate | |
initCounterPrivate value = | |
Private [] | |
-- Update the private data for a counter given a change in | |
-- the public value | |
updateCounterPrivate : Int -> CounterPrivate -> CounterPrivate | |
updateCounterPrivate value old = | |
old | |
-- Generate a view for a counter with value stack | |
viewCounter : Int -> CounterPrivate -> Html CounterMsg | |
viewCounter value (Private stack) = | |
div [] | |
[ button [ onClick Decrement ] [ text "-" ] | |
, div [] [ text (toString value) ] | |
, button [ onClick Increment ] [ text "+" ] | |
, button [ onClick Push ] [ text "Push" ] | |
, button [ onClick Pop, disabled (stack == []) ] [ text "Pop" ] | |
] | |
-- Update for a counter with stack | |
updateCounter : CounterMsg -> Int -> CounterPrivate -> (Int, CounterPrivate) | |
updateCounter msg value ((Private stack) as private) = | |
case msg of | |
Decrement -> | |
(value - 1, private) | |
Increment -> | |
(value + 1, private) | |
Push -> | |
(value, Private <| value :: stack) | |
Pop -> | |
case stack of | |
[] -> (value, private) | |
x :: rest -> (x, Private rest) | |
-- Our model will have two view with private stacks working with | |
-- the same counter | |
type alias Model = | |
{ value : Int | |
, privateA : CounterPrivate | |
, privateB : CounterPrivate | |
} | |
type Msg | |
= ToA CounterMsg | |
| ToB CounterMsg | |
init : Int -> Model | |
init value = | |
{ value = value | |
, privateA = initCounterPrivate value | |
, privateB = initCounterPrivate value | |
} | |
view : Model -> Html Msg | |
view model = | |
div [] | |
[ Html.App.map ToA <| viewCounter model.value model.privateA | |
, Html.App.map ToB <| viewCounter model.value model.privateB | |
] | |
update : Msg -> Model -> Model | |
update msg model = | |
case msg of | |
ToA cmsg -> | |
let | |
(newValue, newPrivateA) = | |
updateCounter cmsg model.value model.privateA | |
model' = | |
setValue newValue model | |
in | |
-- Override any update from setValue | |
{ model' | privateA = newPrivateA } | |
ToB cmsg -> | |
let | |
(newValue, newPrivateB) = | |
updateCounter cmsg model.value model.privateB | |
model' = | |
setValue newValue model | |
in | |
-- Override any update from setValue | |
{ model' | privateB = newPrivateB } | |
-- When we update the shared counter value, we provide an opportunity | |
-- for the private states to update as well. | |
setValue : Int -> Model -> Model | |
setValue newValue model = | |
if newValue == model.value then | |
model | |
else | |
{ model | |
| value = newValue | |
, privateA = updateCounterPrivate newValue model.privateA | |
, privateB = updateCounterPrivate newValue model.privateB | |
} | |
-- Wire it up and run it | |
main = | |
Html.App.beginnerProgram { model = init 0, view = view, update = update } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment