Monthly Archives: July 2016


An Introduction to Elm Series: Solution to ‘Time’ example

In http://guide.elm-lang.org/architecture/effects/time.html, we are given an application that resembles a clock and we are asked to add a button to pause it and add additional hands.

What we did in the following code is:

  • We created a normal clock that shows the time in UTC
  • A toggle button that pauses/resumes it was added
  • We added the second, minute and hour hands. Which we gave them different lengths and colors
  • We created utilities to convert time to angle in degrees both for minutes/seconds and for hours
  • A new function that adds the leading zero to values when we print the time in digital format was created
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import Html exposing (Html, br, div, button)
import Html.Events exposing (onClick)
import Html.App as Html
import Svg exposing (..)
import Svg.Attributes exposing (..)
import Time exposing (Time, second)
 
 
 
main =
  Html.program
    { init = init
    , view = view
    , update = update
    , subscriptions = subscriptions
    }
 
 
 
-- MODEL
 
 
type alias Model =
  { time : Time
  , state : Bool
  }
 
 
init : (Model, Cmd Msg)
init =
  (Model 0 True, Cmd.none)
 
 
 
-- UPDATE
 
 
type Msg
  = Tick Time
  | Toggle
 
 
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    Tick newTime ->
      ({ model | time = newTime }, Cmd.none)
 
    Toggle ->
      (toggleState model, Cmd.none)
 
 
-- UTILITIES
toggleState : Model -> Model
toggleState model =
  {model | state = not(model.state)}
 
printWithLeadingZero : Int -> String
printWithLeadingZero number =
  if number < 10 then
    "0" ++ (toString number)
  else
    toString number
 
degressCorrection : Float
degressCorrection =
  90.0 -- The correction we must do on our analog clock to show 12 pointing up instead to the right
 
degressForHour : Float
degressForHour =
  360.0 / 12.0 -- We divide the total degrees of a full circle by the full hours of the day to get the degrees per hour
 
degreesForMinute : Float
degreesForMinute =
  360.0 / 60.0 -- We divide the total degrees of a full circle by the full minutes of the hour to get the degrees per minute
 
convertToDegrees : Int -> Float -> Float
convertToDegrees value degreesPerPoint =
  degrees (((toFloat value) * degreesPerPoint) - degressCorrection)
 
minutesToDegrees : Int -> Float
minutesToDegrees minutes =
  convertToDegrees minutes degreesForMinute
 
hoursToDegrees : Int -> Float
hoursToDegrees hours =
  convertToDegrees hours degressForHour
 
-- SUBSCRIPTIONS
 
 
subscriptions : Model -> Sub Msg
subscriptions model =
  case model.state of
    True ->
      Time.every second Tick
    False ->
      Sub.none
 
 
 
-- VIEW
 
 
view : Model -> Html Msg
view model =
  let
 
 
    second' =
        Time.inSeconds model.time
 
    secondOfMinute =
        truncate second' `rem` 60
 
    secondAngle =
      minutesToDegrees secondOfMinute
 
    secondHandX =
      toString (50 + 40 * cos secondAngle)
 
    secondHandY =
      toString (50 + 40 * sin secondAngle)
 
    minute' =
      Time.inMinutes model.time
 
    minuteOfHour =
      truncate minute' `rem` 60
 
    minuteAngle =
      minutesToDegrees minuteOfHour
 
    minuteHandX =
      toString (50 + 35 * cos minuteAngle)
 
    minuteHandY =
      toString (50 +35 * sin minuteAngle)
 
    hour' =
      Time.inHours model.time
 
    hourOfDay =
      truncate hour' `rem` 24
 
    hourAngle =
      hoursToDegrees hourOfDay
 
    hourHandX =
      toString (50 + 30 * cos hourAngle)
 
    hourHandY =
      toString (50 + 30 * sin hourAngle)
 
    currentTime =
      (printWithLeadingZero hourOfDay) ++ ":" ++ (printWithLeadingZero minuteOfHour) ++ ":" ++ (printWithLeadingZero secondOfMinute)
 
  in
    div []
      [ div [] [text currentTime]
      , svg [ viewBox "0 0 100 100", width "300px" ]
        [ circle [ cx "50", cy "50", r "45", fill "#0B79CE" ] []
        , line [ x1 "50", y1 "50", x2 secondHandX, y2 secondHandY, stroke "#ee0000" ] []
        , line [ x1 "50", y1 "50", x2 minuteHandX, y2 minuteHandY, stroke "#0000ee" ] []
        , line [ x1 "50", y1 "50", x2 hourHandX, y2 hourHandY, stroke "#023963" ] []
       ; ]
      , br [] []
      , button [onClick Toggle] [text "Toggle Clock Status"]
     ; ]

You can download the solution from here [download id=”1816″]

 


An Introduction to Elm Series: Solution to ‘HTTP’ example 5

In the http://guide.elm-lang.org/architecture/effects/http.html, we were given a sample that loads random images from a web application on another service thought HTTP requests.

We were asked to print the error message of the request (if any) and update the UI to allow changing the topic (a parameter of the web call) either through an input field or a drop down list.

We wrote a message that accepts as string the new topic, that message is used both in the case of the input and in the case of the select list. Finally, we made sure that the ‘Please Wait’ image is shown while loading the new image from the remote server. The path for the ‘Please Wait’ image was added to a custom function to emulate a public static string value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import Html exposing (..)
import Html.App as Html
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Http
import Json.Decode as Json
import Task
 
 
 
main =
  Html.program
    { init = init "cats"
    , view = view
    , update = update
    , subscriptions = subscriptions
    }
 
 
 
-- MODEL
 
 
type alias Model =
  { topic : String
  , gifUrl : String
  , error : String
  }
 
waitingImage : String
waitingImage =
  "5-http/waiting.png"
 
init : String -> (Model, Cmd Msg)
init topic =
  ( Model topic waitingImage ""
  , getRandomGif topic
  )
 
 
 
-- UPDATE
 
 
type Msg
  = MorePlease
  | FetchSucceed String
  | FetchFail Http.Error
  | NewTopic String
 
 
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    MorePlease ->
      ({ model | gifUrl = waitingImage }, getRandomGif model.topic)
 
    FetchSucceed newUrl ->
      (Model model.topic newUrl "", Cmd.none)
 
    FetchFail error ->
      (Model model.topic waitingImage (toString error), Cmd.none)
 
    NewTopic newTopic ->
      ({ model | topic = newTopic }, Cmd.none)
 
 
-- VIEW
 
 
view : Model -> Html Msg
view model =
  div []
    [ h2 [] [text model.topic]
    , input [ type' "text", placeholder "Topic", onInput NewTopic ] []
    , select [ onInput NewTopic ]
      [ option [] [ text "Pokemon" ]
      , option [] [ text "SuperCars" ]
      , option [] [ text "Cyprus" ]
      , option [] [ text "Cats" ]
      , option [] [ text "Dogs" ]
     ; ]
    , button [ onClick MorePlease ] [ text "More Please!" ]
    , br [] []
    , img [src model.gifUrl] []
    , br [] []
    , div [] [text model.error]
   ; ]
 
 
 
-- SUBSCRIPTIONS
 
 
subscriptions : Model -> Sub Msg
subscriptions model =
  Sub.none
 
 
 
-- HTTP
 
 
getRandomGif : String -> Cmd Msg
getRandomGif topic =
  let
    url =
      "//api.giphy.com/v1/gifs/random?api_key=dc6zaTOxFJmzC&tag=" ++ topic
  in
    Task.perform FetchFail FetchSucceed (Http.get decodeGifUrl url)
 
 
decodeGifUrl : Json.Decoder String
decodeGifUrl =
  Json.at ["data", "image_url"] Json.string

You can download the solution from here [download id=”1811″]


An Introduction to Elm Series: Solution to ‘Random’ example 1

In the http://guide.elm-lang.org/architecture/effects/random.html, we were given an application that rolls a die and prints the number of the face that was rolled.

We were asked to add a second die that would be rolled together with the first one and show an image of a die instead of just printing the number on the face of the die.

Following you will find our proposed solution. We added a new value in the model for the second die and updated the message for NewFace to produce a message to roll the second die as well. Also we created a function that accepts the number of the face of the die and produces a path to the image that should be loaded.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import Html exposing (..)
import Html.App as Html
import Html.Events exposing (..)
import Html.Attributes exposing (..)
import Random
import String exposing (concat)
 
 
 
main =
  Html.program
    { init = init
    , view = view
    , update = update
    , subscriptions = subscriptions
    }
 
 
 
-- MODEL
 
 
type alias Model =
  { dieFaceA : Int
  , dieFaceB : Int
  }
 
 
init : (Model, Cmd Msg)
init =
  (Model 1 1, Cmd.none)
 
 
 
-- UPDATE
 
 
type Msg
  = Roll
  | NewFaceA Int
  | NewFaceB Int
 
 
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    Roll ->
      (model, Random.generate NewFaceA (Random.int 1 6))
 
    NewFaceA newFace ->
      ({model | dieFaceA = newFace}, Random.generate NewFaceB (Random.int 1 6))
 
    NewFaceB newFace ->
      ({model | dieFaceB = newFace}, Cmd.none)
 
 
 
-- SUBSCRIPTIONS
 
 
subscriptions : Model -> Sub Msg
subscriptions model =
  Sub.none
 
 
-- UTILITIES
 
 
createImage : Int -> String
createImage dieFace =
    concat ["./4-random/", (toString dieFace), ".png"]
 
 
 
-- VIEW
 
 
view : Model -> Html Msg
view model =
  div []
    [ img [src (createImage model.dieFaceA)] []
    , h1 [] [ text (toString model.dieFaceA) ]
    , img [src (createImage model.dieFaceB)] []
    , h1 [] [ text (toString model.dieFaceB) ]
    , button [ onClick Roll ] [ text "Roll" ]
    ]

You can download the solution from here [download id=”1804″]


An Introduction to Elm Series: Solution to ‘Forms’ example

In http://guide.elm-lang.org/architecture/user_input/forms.html, you are given a simple form that accepts the name of a user and a password along with a validation field for the password.

You are asked to perform the following validations on the form:

  • Check that the password is longer than 8 characters.
  • Make sure the password contains upper case, lower case, and numeric characters.
  • Add an additional field for age and check that it is a number.
  • Add a “Submit” button. Only show errors after it has been pressed.

What we did was to add two new members to our model. One for the validation flag (if we should show the result or not) and one for the age. Then we created two new signals, one for the age field and one for the submit button and updated all signals to reset the validation flag. Afterwards, we added on the GUI the new input elements and attached the appropriate signals. Finally, we performed all checks using the appropriate libraries.

In the following proposed full source code solution we highlight our changes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import Html exposing (..)
import Html.App as Html
import Html.Attributes exposing (..)
import Html.Events exposing (onInput, onClick)
import String exposing (trim, length, isEmpty, toLower, toUpper, any, all)
import Char exposing (isDigit, isLower, isUpper)
 
 
main =
  Html.beginnerProgram
    { model = model
    , view = view
    , update = update
    }
 
 
 
-- MODEL
 
 
type alias Model =
  { name : String
  , password : String
  , passwordAgain : String
  , age : String
  , validate : Bool
  }
 
 
model : Model
model =
  Model "" "" "" "" False
 
 
 
-- UPDATE
 
 
type Msg
    = Name String
    | Password String
    | PasswordAgain String
    | Age String
    | Validate
 
 
update : Msg -> Model -> Model
update msg model =
  case msg of
    Name name ->
      { model | name = name, validate = False }
 
    Password password ->
      { model | password = password, validate = False }
 
    PasswordAgain password ->
      { model | passwordAgain = password, validate = False }
 
    Age age ->
      { model | age = trim age, validate = False }
 
    Validate ->
      { model | validate = True }
 
-- VIEW
 
 
view : Model -> Html Msg
view model =
  div []
    [ input [ type' "text", placeholder "Name", onInput Name ] []
    , input [ type' "password", placeholder "Password", onInput Password ] []
    , input [ type' "password", placeholder "Re-enter Password", onInput PasswordAgain ] []
    , input [ type' "number", placeholder "Age", onInput Age ] []
    , button [ onClick Validate ] [ text "Submit" ]
    , viewValidation model
   ; ]
 
 
viewValidation : Model -> Html msg
viewValidation model =
  let
    (color, message) =
      if model.validate then
        if length model.password < 8 then
          ("red", "Password too short")
        else if any isUpper model.password == False then
        -- Alternatively, instead of checking if there is at least one character that is Upper, we can convert the input toLower and check if it changed
        -- else if model.password == toLower model.password then
          ("red", "Password does not contain an upper case character")
        else if any isLower model.password == False then
        -- Alternatively, instead of checking if there is at least one character that is lower, we can convert the input toUpper and check if it changed
        -- else if model.password == toUpper model.password then
          ("red", "Password does not contain a lower case character")
        else if any isDigit model.password == False then
          ("red", "Password does not contain a numeric character")
        else if model.password /= model.passwordAgain then
          ("red", "Passwords do not match!")
        else if isEmpty model.age || all isDigit model.age == False then
          ("red", "Age is not a positive integer number")
        else ("green", "OK")
      else ("white", "")
  in
    div [ style [("color", color)] ] [ text message ]

You can download the solution from here [download id=”1799″]