# Elm Loadable

This package is an extension of the ideas from this very cool
[project](https://package.elm-lang.org/packages/krisajenkins/remotedata/latest)/[blog](http://blog.jenkster.com/2016/06/how-elm-slays-a-ui-antipattern.html)/[talk](https://www.youtube.com/watch?v=NLcRzOyrH08)
by Kris Jenkins.

```elm
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.

```elm
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)
        ]
```
