A different approach to Kris Jenkins' RemoteData.
- RemoteData.Failure is always sub-divided into transport errors and custom errors;
- An extra module "Recyclable" for reusing information while fresh data is loading;
- There are shorthand types for data loaded using Graphql.
Using Kris' RemoteData, our team realized that we needed to support both the transport errors (e.g., Graphql errors, HTTP errors) and any other custom error sent by our API (e.g., invalid credentials and invalid input).
That way, our models were ending up with types like RemoteData TransportErrorsPlusCustomErrors SomeData
.
With that in mind, and to reduce code repetition, we decided to merge both failures within RemoteData.
Alongside that, we came up with a structure for when some information is reloading, and we do not loose the previously loaded one. We called it Recyclable.
Following our use-case scenarios, we changed the main star from Http.Error
to Graphql.Http.HttpError
instead.
However, support for a different transport protocol is achievable using more abstract types.
From your top-level directory - the one with elm.json
in - call:
$ elm install PaackEng/paack-remotedata
See package's page for the complete documentation.
Here's an example of how to use this package:
-- Graphql queries, data types, and errors unions.
import Api.Author as Author exposing (Author)
import Api.Authors as Authors exposing (AuthorBrief)
-- Elm-UI
import Element exposing (Element)
import Element.Input as Input
-- This package
import Remote.Data as RemoteData exposing (GraphqlHttpData)
import Remote.Errors as RemoteErrors
import Remote.Recyclable as Recyclable exposing (GraphqlHttpRecyclable)
import Remote.Response as Response exposing (GraphqlHttpResponse)
type Msg
= AuthorFetched (GraphqlHttpResponse Author.Error Author)
| AuthorSelect Author.Id
| AuthorsFetched (GraphqlHttpResponse Authors.Error (List AuthorBrief))
| Reload
type alias Model =
{ authors : GraphqlHttpData Authors.Error (List AuthorBrief)
, selectedAuthor : GraphqlHttpRecyclable Author.Error Author
}
init : RequestConfig -> ( Model, Cmd Msg )
init requestConfig =
( { authors = RemoteData.Loading
, selectedAuthor = Recyclable.NotAsked
}
, Authors.request requestConfig AuthorsFetched
)
update : RequestConfig -> Model -> Model -> ( Model, Cmd Msg )
update requestConfig model msg =
case msg of
AuthorFetched response ->
( { model | authors = RemoteData.fromResponse response }
, Cmd.none
)
AuthorSelect id ->
( { model
| selectedAuthor =
Recyclable.toLoading model.selectedAuthor
}
, Author.request requestConfig id AuthorFetched
)
AuthorsFetched response ->
( { model
| selectedAuthor =
Recyclable.mergeResponse
response
model.selectedAuthor
}
, Cmd.none
)
Reload ->
init requestConfig
view : Model -> Element Msg
view model =
Element.column [ Element.width fill ]
[ pickAuthorView model
, selectedAuthorView model
]
pickAuthorView : Model -> Element Msg
pickAuthorView model =
case model.authors of
RemoteData.Success [] ->
Element.text "There is no author available."
RemoteData.Success list ->
List.map
(\{ id, name } ->
Input.button []
{ onPress = AuthorSelect id, label = name }
)
list
RemoteData.Loading ->
Element.text "Loading authors list..."
RemoteData.NotAsked ->
Element.button []
{ onPress = Reload, label = "Reload page" }
RemoteData.Failure (RemoteErrors.Custom Authors.InvalidCredentials) ->
Element.text "You must login to access the author list!"
RemoteData.Failure (RemoteErrors.Transport _) ->
Element.text "Connection lost!"
selectedAuthorView : Model -> Element Msg
selectedAuthorView model =
case model.selectedAuthor of
Recyclable.NeverAsked ->
Element.text "Please, pick an author!"
Recyclable.Loading ->
Element.text "Loading"
Recyclable.Failure _ ->
Element.text "Failed to load author!"
Recyclable.Recycling { name } newState ->
Element.row
[ case newState of
Recyclable.Loading ->
Element.tet "Loading new author..."
Recyclable.Failure _ ->
Element.tet "Unable to load new author!"
, Element.text ("You previously picked: " ++ name)
]
Recyclable.Ready { name } ->
Element.text ("You picked: " ++ name)
Copyright © 2020 Paack
Distributed under the MIT license.