krisajenkins/remotedata/2.4.0

NOTE: this package is not compatible with Elm 0.19.1

RemoteData

A datatype representing fetched data.

If you find yourself continually using Maybe (Result Error a) to represent loaded data, or you have a habit of shuffling errors away to where they can be quietly ignored, consider using this. It makes it easier to represent the real state of a remote data fetch and handle it properly.

For more on the motivation, take a look at the blog post How Elm Slays A UI Antipattern.

To use the datatype, let's look at an example that loads News from a feed.

First you add to your model, wrapping the data you want in WebData:

type alias Model =
    { news : WebData News }

Then add in a message that will deliver the response:

type alias Msg
    = NewsResponse (WebData News)

Now we can create an HTTP get:

getNews : Cmd Msg
getNews =
    Http.get decodeNews "/news"
        |> RemoteData.asCmd
        |> Cmd.map NewsResponse

We trigger it in our init function:

init : ( Model, Cmd Msg)
init =
    ( { news = Loading }
    , getNews
    )

We handle it in our update function:

update msg model =
    case msg of
        NewsResponse response ->
            ( { model | news = response }
            , Cmd.none
            )

Most of this you'd already have in your app, and the changes are just wrapping the datatype in Webdata, and updating the Http.get call to add in RemoteData.asCmd.

Now we get to where we really want to be, rendering the data and handling the different states in the UI gracefully:

view : Model -> Html msg
view model =
  case model.news of
    NotAsked -> text "Initialising."


    Loading -> text "Loading."


    Failure err -> text ("Error: " ++ toString err)


    Success news -> viewNews news




viewNews : News -> Html msg
viewNews news =
    div []
        [h1 [] [text "Here is the news."]
        , ...]

And that's it. A more accurate model of what's happening leads to a better UI.

type RemoteData e a
= NotAsked
| Loading
| Failure e
| Success a

Frequently when you're fetching data from an API, you want to represent four different states: * NotAsked - We haven't asked for the data yet. * Loading - We've asked, but haven't got an answer yet. * Failure - We asked, but something went wrong. Here's the error. * Success - Everything worked, and here's the data.

type alias WebData a =
RemoteData Error a

While RemoteData can model any type of error, the most common one you'll actually encounter is when you fetch data from a REST interface, and get back RemoteData Http.Error a. Because that case is so common, WebData is provided as a useful alias.

map : (a -> b) -> RemoteData e a -> RemoteData e b

Map a function into the Success value.

mapFailure : (e -> f) -> RemoteData e a -> RemoteData f a

Map a function into the Failure value.

mapBoth : (a -> b) -> (e -> f) -> RemoteData e a -> RemoteData f b

Map function into both the Success and Failure value.

andThen : RemoteData e a -> (a -> RemoteData e b) -> RemoteData e b

Chain together RemoteData function calls.

withDefault : a -> RemoteData e a -> a

Return the Success value, or the default.

fromResult : Result e a -> RemoteData e a

Convert a Result Error, probably produced from elm-http, to a RemoteData value.

toMaybe : RemoteData e a -> Maybe a

Convert a RemoteData e a to a Maybe a

asCmd : Task e a -> Cmd (RemoteData e a)

Convert a web Task, probably produced from elm-http, to a Cmd (RemoteData e a).

fromTask : Task e a -> Task Never (RemoteData e a)

Convert from a Task that may succeed or fail, to one that always succeeds with the RemoteData that captures any errors.

append : RemoteData e a -> RemoteData e b -> RemoteData e ( a, b )

Append - join two RemoteData values together as though they were one.

If either value is NotAsked, the result is NotAsked. If either value is Loading, the result is Loading. If both values are Failure, the left one wins.

isSuccess : RemoteData e a -> Bool

State-checking predicate. Returns true if we've successfully loaded some data.

isFailure : RemoteData e a -> Bool

State-checking predicate. Returns true if we've failed to load some data.

update : (a -> ( b, Cmd c )) -> RemoteData e a -> ( RemoteData e b, Cmd c )

Apply an Elm update function - Model -> (Model, Cmd Msg) - to any Successful-ly loaded data.

It's quite common in Elm to want to run a model-update function, over some remote data, but only once it's actually been loaded.

For example, we might want to handle UI messages changing the users settings, but that only makes sense once those settings have been returned from the server.

This function makes it more convenient to reach inside a RemoteData.Success value and apply an update. If the data is not Success a, it is return unchanged with a Cmd.none.

pure : a -> RemoteData e a
apply : RemoteData e (a -> b) -> RemoteData e a -> RemoteData e b

Applicative instance for RemoteData.

If you know what that means, then you know if you need this.

If not, this probably isn't the place for an applicatve tutorial, but the gist is it makes it easy to create functions that merge several RemoteData values together. For example, RemoteData.append is implemented as:

append a b =
    (,) <$> a <*> b
(<$>) : (a -> b) -> RemoteData e a -> RemoteData e b
(<*>) : RemoteData e (a -> b) -> RemoteData e a -> RemoteData e b
{ getOption : RemoteData e a -> Maybe a
, reverseGet : a -> RemoteData e a
}

A monocle-compatible Prism.

If you use Monocle, you'll want this, otherwise you can ignore it.

The type signature is actually:

prism : Prism (RemoteData e a) a

...but we use the more verbose type here to avoid introducing a dependency on Monocle.