Skip to content

Instantly share code, notes, and snippets.

@markhamburg
Created September 10, 2016 22:19
Show Gist options
  • Save markhamburg/e3b9af785d06ed392e4498e569045b25 to your computer and use it in GitHub Desktop.
Save markhamburg/e3b9af785d06ed392e4498e569045b25 to your computer and use it in GitHub Desktop.
Move the private state into a dictionary. I feel like I'm starting to re-invent elm-parts/elm-mdl.
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.App
import Html.Events exposing (..)
import Dict
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 :
(Int -> parentMsg) -- config: send setValue messages back
-> Int -- the current counter value
-> CounterMsg -- our message
-> CounterPrivate -- our state
-> (CounterPrivate, Maybe parentMsg)
updateCounter setValue value msg ((Private stack) as private) =
case msg of
Decrement ->
(private, Just <| setValue <| value - 1)
Increment ->
(private, Just <| setValue <| value + 1)
Push ->
(Private <| value :: stack, Nothing)
Pop ->
case stack of
[] -> (private, Nothing)
x :: rest -> (Private rest, Just <| setValue x)
-- Our model will have two view with private stacks working with
-- the same counter
type alias Model =
{ value : Int
, counterPrivate : Dict.Dict String CounterPrivate
}
type Msg
= ToCounter String CounterMsg
| SetValue Int
init : Int -> Model
init value =
{ value = value
, counterPrivate = Dict.empty
}
view : Model -> Html Msg
view model =
div []
[ viewCounterWithIdent "A" model
, viewCounterWithIdent "B" model
]
viewCounterWithIdent : String -> Model -> Html Msg
viewCounterWithIdent ident model =
Html.App.map (ToCounter ident) <| viewCounter model.value (getCounterPrivate ident model)
update : Msg -> Model -> Model
update msg model =
case msg of
ToCounter ident cmsg ->
let
counterPrivate =
getCounterPrivate ident model
(newPrivate, maybeMsg) =
updateCounter SetValue model.value cmsg counterPrivate
in
model
|> maybeUpdate maybeMsg
|> setCounterPrivate ident newPrivate
SetValue newValue ->
if model.value == newValue then
model
else
{ model
| value = newValue
, counterPrivate =
model.counterPrivate
|> Dict.map (\key private -> updateCounterPrivate newValue private)
}
maybeUpdate maybeMsg model =
maybeMsg
|> Maybe.map (\msg -> update msg model)
|> Maybe.withDefault model
-- Create the private data for counters on demand. We will store
-- them if they actually change.
getCounterPrivate : String -> Model -> CounterPrivate
getCounterPrivate key model =
case Dict.get key model.counterPrivate of
Just x -> x
Nothing -> initCounterPrivate model.value
setCounterPrivate : String -> CounterPrivate -> Model -> Model
setCounterPrivate ident private model =
{ model | counterPrivate = Dict.insert ident private model.counterPrivate }
-- 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