Skip to content

Instantly share code, notes, and snippets.

@piotrkubisa
Last active July 25, 2016 18:27
Show Gist options
  • Select an option

  • Save piotrkubisa/d5225c285bd98eb801a2ca9ac4028283 to your computer and use it in GitHub Desktop.

Select an option

Save piotrkubisa/d5225c285bd98eb801a2ca9ac4028283 to your computer and use it in GitHub Desktop.
Recursive module in Elm with update
module Categories exposing (Model, Msg, init, update, view)
import Html exposing (..)
import Html.Attributes exposing (style)
import Html.Events exposing (onInput, onClick)
type alias Model =
{ entry : String
, path : Path
, children : Children
}
type Children
= Children (List Model)
type alias Path =
List Int
init : Model
init =
{ entry = "hello world"
, path = []
, children =
Children
[ { entry = "hello2"
, path = [ 0 ]
, children =
Children
[ { entry = "hello 3", path = [ 0, 0 ], children = Children [] }
, { entry = "hello 4", path = [ 0, 1 ], children = Children [] }
, { entry = "hello 5", path = [ 0, 2 ], children = Children [] }
]
}
, { entry = "hello 6", path = [ 1 ], children = Children [] }
, { entry = "hello 7", path = [ 2 ], children = Children [] }
]
}
model : Model
model =
init
wrap : List Model -> Children
wrap children =
(Children children)
unwrap : Children -> List Model
unwrap (Children children) =
children
createTask : String -> Path -> Model
createTask entry path =
{ entry = entry
, path = path
, children = Children []
}
{-|
Not used
-}
prependTask : String -> Path -> Children -> Children
prependTask entry path (Children children) =
let
task =
createTask entry path
in
Children (task :: children)
appendTask : String -> Path -> Children -> Children
appendTask entry path (Children children) =
let
task =
createTask entry path
in
wrap (children ++ [ task ])
-- UPDATE
type Msg
= NoOp
| Write String
| WriteTo Model String
| Append Path
| AppendTo Model
update : Msg -> Model -> Model
update msg model =
case msg of
Write newEntry ->
{ model | entry = newEntry }
WriteTo item newEntry ->
updateAt update item.path (Write newEntry) model
Append path ->
{ model | children = (appendTask model.entry path (model.children)) }
AppendTo item ->
let
path =
case List.tail (item.path) of
Nothing ->
[ (List.length (unwrap item.children)) ]
Just pos ->
item.path ++ [ (List.length (unwrap item.children)) ]
in
updateAt update item.path (Append path) model
NoOp ->
model
{-|
That snippet was made by @szabba from Elm's Slack
-}
updateAt : (a -> Model -> Model) -> Path -> a -> Model -> Model
updateAt update path msg model =
case path of
[] ->
update msg model
branch :: rest ->
let
newChildren =
model.children
|> childrenIxMap
(\ix child ->
if ix == branch then
updateAt update rest msg child
else
child
)
in
{ model | children = newChildren }
childrenIxMap : (Int -> Model -> Model) -> Children -> Children
childrenIxMap f (Children children) =
children
|> List.indexedMap f
|> Children
-- VIEW
view : Model -> Html Msg
view model =
div []
[ ul [] [ (viewItem model) ]
]
viewList : Children -> List (Html Msg)
viewList (Children children) =
List.map viewItem children
viewItem : Model -> Html Msg
viewItem item =
li []
[ div [] [ text item.entry ]
, (viewForm item)
, ul [] (viewList item.children)
]
viewForm : Model -> Html Msg
viewForm item =
let
btnLabel =
"append to " ++ (toString item.entry) ++ " path: " ++ (toString item.path)
in
div [ style [] ]
[ input [ style [], onInput (WriteTo item) ] []
, button [ style [], onClick (AppendTo item) ] [ text btnLabel ]
]
@szabba
Copy link

szabba commented Jul 24, 2016

You could try something like

type alias Model =
    { entry : String
    , children : Children
    }


type Children
    = Children (List Model)

type alias Path =
    List Int


updateAt : (a -> Model -> Model) -> Path -> a -> Model -> Model
updateAt update path msg model =
    case path of

        [] ->
            update msg model

        branch :: rest ->
            let
                newChildren =
                    model.children
                        |> childrenIxMap
                            (\ix child ->
                                if ix == branch then
                                    updateAt update rest msg child
                                else
                                    child
                            )
            in
                { model | children = newChildren }



childrenIxMap : (Int -> Model -> Model) -> Children -> Children
childrenIxMap f (Children children) =
    children
        |> List.indexedMap f
        |> Children

@piotrkubisa
Copy link
Author

Thank you! I have updated to make it working with Append/Write events

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment