opvasger/loadable

Intuitive data-loading in Elm!


License
BSD-3-Clause
Install
elm-package install opvasger/loadable 2.1.0

Documentation

Elm Loadable

This package is an extension of the ideas from this very cool project/blog/talk by Kris Jenkins.

type Loadable error value
    = Idle
    | Loading
    | Success value
    | Failure error
    | Reloading value
    | ReloadFailure error value

It's nice to have context available during data-loading - this package keeps the previous value around until a new one arrives. It can be shown during (or after failing) to reload.

module Main exposing (main)

import Browser exposing (element)
import Element exposing (Element, centerX, centerY, column, el, layout, px, rgb255, text, width)
import Element.Font exposing (color, size)
import Element.Input as I exposing (labelHidden)
import Http exposing (Error, expectJson, get)
import Json.Decode exposing (field, map2, string)
import Loadable as L exposing (Loadable(..), expectUpdate)
import Time exposing (every)

type alias Repo = { name : String, tagline : String }
type Msg = InputAuthor String | InputProject String | PassedSecond | GotRepo (Result Error Repo)

main : Program () { author : String, project : String, repo : Loadable Error Repo, timeout : Int } Msg
main = element { init = init, view = layout [] << view, update = update, subscriptions = subscriptions }

init _ = ( { author = "opvasger", project = "loadable", repo = Loading, timeout = 2 }, Cmd.none )

subscriptions { timeout } = if timeout > 0 then every 1000 (always PassedSecond) else Sub.none

update msg ({ repo, author, project, timeout } as model) =
    case msg of
        InputAuthor name ->     ( { model | repo = expectUpdate repo, author =  name, timeout = 2 }, Cmd.none )
        InputProject name ->    ( { model | repo = expectUpdate repo, project = name, timeout = 2 }, Cmd.none )
        GotRepo result ->       ( { model | repo = L.update result repo                           }, Cmd.none )
        PassedSecond ->
            ( { model | timeout = timeout - 1 }, if timeout == 1 then 
                    get { url = "https://api.github.com/repos/" ++ author ++ "/" ++ project
                        , expect = expectJson GotRepo (map2 Repo (field "name" string) (field "description" string))
                        }
              else Cmd.none )

view { author, project, repo } =
    let (r,g,b) = if L.isLoading repo then (  0,  0,255)
             else if L.hasError  repo then (255,  0,  0)
             else                          (  0,255,  0) in
    column [ width (px 300), centerX, centerY ]
        [ I.text [ color (rgb255 r g b) ] { onChange = InputAuthor,  text = author,  placeholder = Nothing, label = labelHidden "author" }
        , I.text [ color (rgb255 r g b) ] { onChange = InputProject, text = project, placeholder = Nothing, label = labelHidden "project" }
        , L.withDefault (text "...") (L.map (\{ name, tagline } -> column [] [ el [ size 50 ] (text name), el [ size 25 ] (text tagline) ]) repo)
        ]