├── .gitignore ├── elm.json ├── README.md └── src └── Main.elm /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | 4 | elm-stuff/ 5 | -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src" 5 | ], 6 | "elm-version": "0.19.0", 7 | "dependencies": { 8 | "direct": { 9 | "elm/browser": "1.0.1", 10 | "elm/core": "1.0.2", 11 | "elm/html": "1.0.0" 12 | }, 13 | "indirect": { 14 | "elm/json": "1.1.3", 15 | "elm/time": "1.0.0", 16 | "elm/url": "1.0.0", 17 | "elm/virtual-dom": "1.0.2" 18 | } 19 | }, 20 | "test-dependencies": { 21 | "direct": {}, 22 | "indirect": {} 23 | } 24 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # elm-from-ground-up 2 | 3 | Welcome to Razoyo's Elm learning lab. The approach of this lab is to start with the most basic possible Elm hello world and build to use more and more features of the language progressively. 4 | 5 | The idea is that you can start playing with a limited set of features and gradually add more data types and functionality as you progress. 6 | 7 | ## Lab 12 - Edit Item 8 | This completes out the use of the Dict data type as part of the model and some basic refactoring. 9 | 10 | ## How to use the lab 11 | 12 | The lab is organized in numbered, step-wise versions. We recommend cloning the repository onto your development environment and check out each numbered branch successively. 13 | 14 | To run the demo simply run `elm reactor` from the project root and navigate to src/Main.elm. 15 | 16 | ## Contributing 17 | 18 | Feel free to submit pull requests. We will attempt to review and either comment on them or merge them in a relatively timely manner. The repository is managed by volunteers with day jobs, so, please be patient. 19 | 20 | To report bugs or request feature enhancements, just open up an issue. 21 | 22 | Happy Elming! 23 | -------------------------------------------------------------------------------- /src/Main.elm: -------------------------------------------------------------------------------- 1 | import Browser 2 | import Html exposing ( Html, text, p, button, div, input, span, form, h1 ) 3 | import Html.Events exposing ( onClick, onInput, onSubmit ) 4 | import Html.Attributes exposing ( value, style ) 5 | import Dict 6 | 7 | 8 | main = 9 | Browser.sandbox 10 | { init = init 11 | , update = update 12 | , view = view 13 | } 14 | 15 | 16 | initialModel = 17 | { items = Dict.fromList [ ( 1, Item "Hello World" Original False 11 ) 18 | , ( 2, Item "Here I Am" Original False 9 ) 19 | ] 20 | , newItem = "" 21 | , sortBy = Order 22 | } 23 | 24 | 25 | emptyItem = 26 | Item "N/A" Original False 0 27 | 28 | 29 | newKey : Dict.Dict Int Item -> Int 30 | newKey items = 31 | Dict.keys items 32 | |> List.maximum 33 | |> Maybe.withDefault 0 34 | |> (\x -> x + 1) 35 | 36 | 37 | init : Model 38 | init = 39 | initialModel 40 | 41 | 42 | 43 | -- MODEL 44 | type alias Model = 45 | { items : Dict.Dict Int Item 46 | , newItem : String 47 | , sortBy : SortOperation 48 | } 49 | 50 | 51 | type Msg = ToggleCase Int 52 | | Reset 53 | | UpdateNew String 54 | | AddNew 55 | | Delete Int 56 | | Sort SortOperation 57 | | EditItem Int 58 | | UpdateItem Int Item String 59 | | StopEdit 60 | 61 | 62 | type SortOperation = Order | Asc | Desc | AscLength | DescLength 63 | 64 | 65 | type DisplayStatus = Original | Capitalized 66 | 67 | 68 | type alias Item = 69 | { item : String 70 | , displayStatus : DisplayStatus 71 | , editing : Bool 72 | , length : Int 73 | } 74 | 75 | 76 | 77 | -- UPDATE 78 | update : Msg -> Model -> Model 79 | update msg model = 80 | case msg of 81 | 82 | ToggleCase key -> 83 | let 84 | newStatus currentStatus = 85 | case currentStatus of 86 | Capitalized -> Original 87 | Original -> Capitalized 88 | 89 | updatedItem = Dict.get key model.items 90 | |> Maybe.withDefault emptyItem 91 | |> (\x -> Item x.item (newStatus x.displayStatus) x.editing x.length) 92 | in 93 | 94 | { model | items = Dict.insert key updatedItem model.items } 95 | 96 | Reset -> 97 | initialModel 98 | 99 | UpdateNew item -> 100 | { model | newItem = item } 101 | 102 | AddNew -> 103 | addNewItem model 104 | 105 | Delete item -> 106 | { model | items = ( Dict.remove item model.items ) } 107 | 108 | Sort operation -> 109 | { model | sortBy = operation } 110 | 111 | EditItem key -> 112 | let 113 | resetItems items = Dict.map (\k v -> { v | editing = False } ) items 114 | 115 | updatedItem = Dict.get key model.items 116 | |> Maybe.withDefault emptyItem 117 | |> (\x -> Item x.item x.displayStatus (not x.editing) x.length) 118 | in 119 | 120 | { model | items = Dict.insert key updatedItem (resetItems model.items) } 121 | 122 | UpdateItem key item newItem -> 123 | { model | items = Dict.insert key { item | item = newItem } model.items } 124 | 125 | StopEdit -> 126 | -- { model | items = Dict.map (\k v -> Item v.item v.displayStatus False v.length ) model.items } 127 | { model | items = Dict.map (\k v -> { v | editing = False } ) model.items } 128 | 129 | addNewItem : Model -> Model 130 | addNewItem model = 131 | let 132 | newItem = model.newItem 133 | items = model.items 134 | in 135 | { model | items = Dict.insert ( newKey items ) ( Item newItem Original False ( String.length newItem ) ) items 136 | , newItem = "" 137 | } 138 | 139 | 140 | -- VIEW 141 | view : Model -> Html Msg 142 | view model = 143 | div [] [ h1 [] [ text ( headList model.items ) ] 144 | , div [] [ span [] [ text "sort operation : " ] 145 | , button [ onClick ( Sort Order ) ] [ text "order" ] 146 | , button [ onClick ( Sort Asc ) ] [ text "a - z" ] 147 | , button [ onClick ( Sort Desc ) ] [ text "z - a" ] 148 | , button [ onClick ( Sort AscLength ) ] [ text "short - long" ] 149 | , button [ onClick ( Sort DescLength ) ] [ text "long - short" ] 150 | ] 151 | , div [] ( sortedList model.items model.sortBy ) 152 | , form [ onSubmit AddNew ] [ input [ value model.newItem, onInput UpdateNew ] [] 153 | , button [ style "margin-left" "10px" ] [ text "submit" ] 154 | ] 155 | , button [ onClick Reset ] [ text "reset" ] 156 | ] 157 | 158 | 159 | headList : Dict.Dict Int Item -> String 160 | headList items = 161 | let 162 | item = items 163 | |> Dict.toList 164 | |> List.head -- this will produce a Maybe condition since a list may be empty 165 | in 166 | 167 | case item of 168 | Just data -> -- if the list is not empty 169 | let 170 | ( k , v ) = data 171 | firstItem = v.item 172 | in 173 | "List starting with \"" ++ firstItem ++ "\"" 174 | 175 | Nothing -> -- if the list is empty 176 | "Empty List" 177 | 178 | 179 | sortedList : Dict.Dict Int Item -> SortOperation -> List ( Html Msg ) 180 | sortedList items sortBy = 181 | 182 | let 183 | itemList = 184 | items 185 | |> Dict.map itemElement 186 | |> Dict.values 187 | 188 | itemElement k v = 189 | let 190 | deleteButton = 191 | span [ style "margin-left" "10px" 192 | , style "color" "red" 193 | , style "font-family" "sans-serif" 194 | , onClick ( Delete k ) 195 | ] [ text "x" ] 196 | in 197 | { element = 198 | div [] [ if v.editing == False then 199 | div [] [ 200 | span [ onClick ( ToggleCase k ) ] [ text ( applyDisplayMode v ) ] 201 | , button [ onClick ( EditItem k ) ] [ text "edit" ] 202 | , deleteButton 203 | ] 204 | else 205 | form [onSubmit StopEdit] [ 206 | input [ value v.item, onInput ( UpdateItem k v ) ] [] 207 | , button [] [ text "save" ] 208 | , deleteButton 209 | ] 210 | ] 211 | , item = v.item 212 | , length = v.length } 213 | in 214 | 215 | case sortBy of 216 | Order -> 217 | itemList 218 | |> List.map .element 219 | 220 | Asc -> 221 | itemList 222 | |> List.sortBy .item 223 | |> List.map .element 224 | 225 | Desc -> 226 | itemList 227 | |> List.sortBy .item 228 | |> List.map .element 229 | |> List.reverse 230 | 231 | AscLength -> 232 | itemList 233 | |> List.sortBy .length 234 | |> List.map .element 235 | 236 | DescLength -> 237 | itemList 238 | |> List.sortBy .length 239 | |> List.map .element 240 | |> List.reverse 241 | 242 | 243 | applyDisplayMode : Item -> String 244 | applyDisplayMode item = 245 | case item.displayStatus of 246 | Capitalized -> 247 | String.toUpper item.item 248 | 249 | _ -> 250 | item.item 251 | --------------------------------------------------------------------------------