Μηνιαία αρχεία: Ιούλιος 2016


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.

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 [] 
    , 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 [] 
    ]



-- 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 5-http (compressed) (311 downloads)


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.

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 4-random (compressed) (281 downloads)


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.

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 3-form.elm (compressed) (309 downloads)


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

In http://guide.elm-lang.org/architecture/user_input/buttons.html, you are given a working code that produces a simple counter which can be incremented or decremented via two buttons on an HTML page.

Your task is to add a new feature to this code, which is to create a new button that will reset the counter. Following you will find a proposed solution to the task. What we did was to create a new message which when received it would zero the model value and then we attached it to a new button we created.

The full solution is as follows and we highlighted all the lines that were added for this additional functionality to work:

import Html exposing (Html, button, div, text)
import Html.App as Html
import Html.Events exposing (onClick)



main =
  Html.beginnerProgram
    { model = model
    , view = view
    , update = update
    }



-- MODEL


type alias Model = Int


model : Model
model =
  0



-- UPDATE


type Msg
  = Increment
  | Decrement
  | Reset


update : Msg -> Model -> Model
update msg model =
  case msg of
    Increment ->
      model + 1

    Decrement ->
      model - 1

    Reset ->
      0

-- VIEW


view : Model -> Html Msg
view model =
  div []
    [ button [ onClick Decrement ] [ text "-" ]
    , div [] [ text (toString model) ]
    , button [ onClick Increment ] [ text "+" ]
    , button [ onClick Reset ] [ text "Reset" ]
    ]

You can download the solution from here 1-button.elm (compressed) (318 downloads)


Create an HTML page with no JavaScript that will redirect the user after a few seconds

The following sample page will redirect the user to bytefreaks.net after 5 seconds. This page does not require JavaScript to be enabled on the user’s browser.

To modify the delay time and the redirect path, you need to edit the following line in the head of the page <meta http-equiv="refresh" content="5;URL=http://www.bytefreaks.net/">. In this example we set the delay to 5 seconds and the redirect url to be http://www.bytefreaks.net/.

You can download a working example of this code here ( DelayedRedirect.html (compressed) (498 downloads) ). If you rename the file to index.html and place it in a folder, it will be the first file that your webserver will read and the redirect will be applied.

<!doctype html>

<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Bytefreaks.net Redirect Page</title>
    <meta name="description" content="A page that will redirect the user to bytefreaks.net after 5 seconds">
    <meta name="author" content="Bytefreaks.net">
    <meta http-equiv="refresh" content="5;URL=http://www.bytefreaks.net/">
  </head>

  <body bgcolor="#ffffff">
    <center>You will be automatically redirected to <a href="http://bytefreaks.net">bytefreaks.net</a> as this resource is not available.</center>
  </body>
</html>