Skip to content

Instantly share code, notes, and snippets.

@ericgj
Last active October 5, 2016 01:28
Show Gist options
  • Save ericgj/7adf98715af89aebd1a2dc73433f5094 to your computer and use it in GitHub Desktop.
Save ericgj/7adf98715af89aebd1a2dc73433f5094 to your computer and use it in GitHub Desktop.
import Html exposing (Html, div, button, text)
import Html.Attributes as Attr
import Html.Events as Evt
import Html.App exposing (beginnerProgram)
import Html.Events exposing (onClick)
import Json.Decode as Json
import String
sample : Person
sample = { name = "Thor", age = 54, password = "#NDkds_(", optional = Nothing }
main : Program Never
main =
beginnerProgram { model = init (Just sample), view = view, update = update }
type alias Person =
{ name : String, age : Int, password : String, optional: Maybe String }
type alias Model =
{ person : Maybe Person
, personAge : Result (String,String) Int
, personName : Result (String,String) String
, personPassword : Result (String,String) String
, personOptional : Result (String,String) (Maybe String)
, dirty : Bool
}
init : Maybe Person -> Model
init mperson =
{ person = mperson
, personAge =
Maybe.map .age mperson
|> Result.fromMaybe ("Please enter your age","")
, personName =
Maybe.map .name mperson
|> Result.fromMaybe ("Please enter your name","")
, personPassword =
Maybe.map .password mperson
|> Result.fromMaybe ("Please enter your password","")
, personOptional =
Maybe.map .optional mperson
|> Result.fromMaybe ("Comments if you like","")
, dirty = False
}
onBlurWithValue tagger =
Evt.on "blur" (Json.map tagger Evt.targetValue)
view : Model -> Html Msg
view model =
div []
[ div []
[ Html.input
[ Attr.placeholder "age"
, Attr.type' "number"
, Attr.value (valueFromResult toString model.personAge)
, onBlurWithValue (validate okAge >> UpdateAge)
]
[]
, Html.text <| resultToString model.personAge
]
, div []
[ Html.input
[ Attr.placeholder "name"
, Attr.value (valueFromResult identity model.personName)
, onBlurWithValue (validate okName >> UpdateName)
]
[]
, Html.text <| resultToString model.personName
]
, div []
[ Html.input
[ Attr.placeholder "password"
, Attr.type' "password"
, Attr.value (valueFromResult identity model.personPassword)
, onBlurWithValue (validate okPassword >> UpdatePassword)
]
[]
, Html.text <| resultToString model.personPassword
]
, div []
[ Html.input
[ Attr.placeholder "optional"
, Attr.value (valueFromResult (Maybe.withDefault "") model.personOptional)
, onBlurWithValue (emptyToResult >> UpdateOptional)
]
[]
, Html.text <| resultToString model.personOptional
]
, button
[ Attr.disabled (model.person == Nothing)
, Evt.onClick SavePerson
]
[ text "Save me" ]
, div [ Attr.style [("color","red")] ]
[ (case model.person of
Just person ->
Html.text (toString person)
_ ->
Html.text "Couldn't save you yet"
)
]
]
type Msg
= UpdateAge (Result (String,String) Int)
| UpdateName (Result (String,String) String)
| UpdatePassword (Result (String,String) String)
| UpdateOptional (Result (String,String) (Maybe String))
| SavePerson
update :
Msg
-> Model
-> Model
update msg model =
case msg of
UpdateAge age ->
let newmodel =
{ model | dirty = True, personAge = age }
in
{ newmodel | person = createPerson newmodel }
UpdateName name ->
let newmodel =
{ model | dirty = True, personName = name }
in
{ newmodel | person = createPerson newmodel }
UpdatePassword pw ->
let newmodel =
{ model | dirty = True, personPassword = pw }
in
{ newmodel | person = createPerson newmodel }
UpdateOptional opt ->
let newmodel =
{ model | dirty = True, personOptional = opt }
in
{ newmodel | person = createPerson newmodel }
SavePerson ->
{ model | dirty = False }
createPerson : Model -> Maybe Person
createPerson model =
Result.map4 Person
model.personName
model.personAge
model.personPassword
model.personOptional
|> Result.toMaybe
resultToString : Result (String,String) b -> String
resultToString result =
case result of
Ok _ ->
"All good"
Err (msg, _) ->
msg
isErr : Result a b -> Bool
isErr result =
case result of
Err _ ->
True
_ ->
False
-- always Ok, convert empty strings to Nothing
emptyToResult : String -> Result (String,String) (Maybe String)
emptyToResult s =
if String.length s == 0 then Ok Nothing
else Ok (Just s)
-- Get either the Ok value (converted to String),
-- or the last input string from the result
valueFromResult : (a -> String) -> Result (String,String) a -> String
valueFromResult formatter r =
case r of
Ok a -> formatter a
Err (_,last) -> last
-- Put the last input string into the result
validate :
(String -> Result String a) ->
String ->
Result (String,String) a
validate func str =
(func str) |> Result.formatError (\err -> (err, str))
okName : String -> Result String String
okName name =
if String.length name > 3 then
Ok name
else
Err "Name must be more than 3 characters."
okAge : String -> Result String Int
okAge age =
Result.andThen
(String.toInt age
|> Result.formatError (\s -> "You must provide an age.")
)
(\age ->
if age > 10 then
Ok age
else
Err "Age must be greater than 10."
)
okPassword : String -> Result String String
okPassword pw =
if String.length pw > 7 then
Ok pw
else
Err "Name must be more than 7 characters."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment