Giter Club home page Giter Club logo

remotedata's Introduction

RemoteData for Elm

Build Status

Tools for fetching data from remote sources (incl. HTTP). For a full discussion of what this package does and why it exists, read this post.

Installation

From your top-level directory - the one with elm.json in - call:


$ elm install krisajenkins/remotedata

Documentation

See the Elm package for full usage docs.

Building & Testing

yarn
yarn run elm make
yarn run elm-test

...will run the whole build.

License

Copyright © 2015-2018 Kris Jenkins

Distributed under the MIT license.

remotedata's People

Contributors

ahstro avatar dillonkearns avatar julianjelfs avatar krisajenkins avatar lpil avatar mthadley avatar nmk avatar ohanhi avatar robertjlooby avatar tbash avatar thomasweiser avatar toastal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

remotedata's Issues

`andMap` only cases on `RemoteData a (b -> c)`

Howdy! Thanks for the great package!

The documentation states that it cases on both values. But the source looks only at the RemoteData a (b -> c):

andMap : RemoteData e a -> RemoteData e (a -> b) -> RemoteData e b
andMap wrappedValue wrappedFunction =
case wrappedFunction of
Success f ->
map f wrappedValue
Failure error ->
Failure error
Loading ->
Loading
NotAsked ->
NotAsked

Is that intentional or a bug? If it's a bug, expect a PR.

Bit confusing as the PS version cases on both values:

https://github.com/krisajenkins/purescript-remotedata/blob/e2c806f732d598d8842e2fe60edc642b4be69713/src/Network/RemoteData.purs#L56-L65

Codecs

For the purpose of caching, encoders and decoders would be awesome.

Generalize RemoteData for PUT, POST and DELETE

Hi @krisajenkins!

First of all thank you for such a great concept and library. It makes it so much better to work with over http data using your library!

Recently I was using RemoteData to represent state of a POST request in my forms. The reason for that is that I need same 4 states which do not fit in just Result or Maybe Result.

This use case led me to thought that there could be a better, more generic names for RemoteData and it's labels. Currently names heavily imply usage alongside GET requests. Which makes code a bit misleading if it is used for POST/PUT.

Let's take a look on the example. Let's say we have a login form. Then we can model it this way:

type alias Model =
    { username : String
    , password : String
    , requestState : RemoteData.WebData ()
    }

init : Model
init =
    { username : ""
    , password : ""
    , requestState = RemoteData.NotAsked
    }

It is working nicely this way. Now we are able to understand in which state our request is and show spinner/error/nothing based on what our requestState is.

However it starts looking a bit weird since we are not talking abount any "data" here per say. We are only interested in the request state.

Specifically names NotAsked, Loading and RemoteData feels misleading to me in this scenario. I don't ask any data and thus it is not loading, but rather request is processing.

So rough example would be type RemoteRequest = NotSent | Processing | Failed | Succeeded feels a bit more correct. Of course names are subject to discussion. The point is - do you feel that this can make sense to do?

Note

If other folks are planing to participate in this discusson, please, omit naming suggestions. The purpose is first of all to discuss general idea and if it's worth making proposed changes. Naming is a subject for separate discussion.

Carrying extra information in `RemoteData.Loading`?

Consider a simple SPA, where you have two buttons, "Load Next" and "Load Previous",
and if I click one of them, I expect the one been clicked change it's text to "Loading", and the other one stay put.

Currently, if you want to achieve this, then you need to track the event source (which button gets clicked) in a separate filed somewhere in the model, maybe something like {remoteData : (Maybe ButtonId, RemoteDate.WebData SomeData)}. However, this has a problem, namely, it becomes a Cartesian product, and not every member in the set makes sense, like, when the state is(Nothing, RemoteData.Loading), we always expect a Just buttonId, not Nothing, (assuming you are interested in which button gets clicked). (In case you are wondering, you cannot change the type from Maybe ButtonId to ButtonId, because when you are setting up the initial model, you will need a placeholder there.)

Any opinion?

Setting fields on a Model with a WebData type

Apologies if this doesn't belong.

I'm confused as to how to set onInput attributes on a model when it has the type WebData - before it gets sent to the server. Here's an example using elm-mdl. I'm not sure how to set an empty record on the initialModel with a WebData type, and when I try to update the model attributes, it's has a type WebData, so it throws an error.

I'm new to Elm, so I think I'm missing something obvious.


type alias Install =
    { pgUser : String }

install = Install ""

type alias Model =
  { mdl : Material.Model
  , install : WebData Install }

initialModel : Model
initialModel =
  { mdl = Material.model
--, install = RemoteData.succeed install
  , install = RemoteData.NotAsked }

type Msg =  ChangePgUser String

installForm : Model -> Html Msg
installForm model = form [ Attr.id "installation__body"
                   , Attr.class "install__form"
                   , Attr.action "/install"
                   , Attr.method "POST" ] [ installHeader "PostgreSQL Configuration"
                                          , Textfield.render Mdl [0] model.mdl
                                            [ Textfield.label "PostgreSQL User"
                                            , Textfield.floatingLabel
                                            , Textfield.text_
                                            , Options.id "pg-user"
                                            , Options.attribute  <| (Attr.attribute "autocomplete" "new-user")
                                            , Options.onInput ChangePgUser
                                            ][]

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model  =
  case msg of
    ChangePgUser username ->
      let
       -- What to do here?
        oInstall =  model.install
        nInstall = { oInstall | pgUser = username }
      in
        ({ model | install = nInstall }, Cmd.none)

And the error (handling a WebData type)

`oInstall` is being used in an unexpected way.

nInstall = { oInstall | pgUser = username }

Based on its definition, `oInstall` has this type:

    WebData Install

But you are trying to use it as: 

    { b | pgUser : a }

RemoteData.toResult

There is fromMaybe

There is toMaybe

There is fromResult

But toResult is missing.

It would make sense in my project. I'm doing some operations on data, that is initially loaded from remote. But other operations afterwards can fail, too. I would like to know the error case. Therefore Maybe is not an option. toResult would be nice.

Is it missing because it's not clear how to handle NotAsked and Loading ? It might make sense to intentionally not have it as a part of the library.

RemoteData won't change to Loading

If I

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

then a NewsResponse RemoteData.Loading won't be delivered to my update function.

Judging from the source, seems to be an intended behaviour, wouldn't deliver a Loading message be better?

How to get error text with elm 0.19?

Hi

The documentation still contains:

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

However toString is not available any more in elm 0.19. It was replaced with Debug.toString, String.fromInt and String.fromFloat. Debug.toString is not available outside of debug mode.

So how can I get the error now?

Update to new elm/http 2.0 API

Elm/http got updated to a new API.

Before

 Http.send LoadMetadata (Http.getString "https://example.com/books/war-and-peace")

After

 Http.get {
  url =  "https://example.com/books/war-and-peace"
  , expect = Http.expectString LoadMetadata
 }

The most dramatic change is that there is no longer a request object. For custom requests, there is still Http.request

Sadly this mean RemoteData.sendRequest no longer works.

Possible new RemoteData api

  1. Mirror the HTTP 2.0 api:
RemoteData.get
RemoteData.post
RemoteData.request
...
  1. Provide a more HTTP 1.0 api:
RemoteData.get |> RemoteData.sendRequest
RemoteData.post |> RemoteData.sendRequest
...

@krisajenkins Do you have any thoughts?

Expose Success Failure Loading and NotAsked

Shouldn't this package expose Success Failure Loading and NotAsked so that we can handle each case appropriately?

I believe that was also the suggestion in the blog post, to have a case statement where we deal with each of these .

Manually create a failure?

Hi! There are situations where I know that the HTTP call the user tries to make will fail. The most common is when the user's session has expired and the request will result in a 401/403.

What I'd like to do is not sent the request but update the model to display a failure. Something a bit like this?

case model.token of
    NotAsked -> { model | sources = Failure "You are not signed in" }
    Failure _ -> { model | sources = Failure "You are not signed in" }
    Loading -> { model | sources = Loading }
    Success t -> { model | sources = Loading }

Is this the right approach? How do I achieve this?

Some sort of withDefault for the failure state

The combo of RemoteData.map plus RemoteData.map is really common and useful. I just faced a situation that I would want to do the same, but caring about the Failure instead of Success case.

I have some typed error that I decode from the request and that I map to the Failure, then having the combo RemoteData.map plus RemoteData.withDefaultForError (something like that with a better name) It would be really nice. Do you think it worth adding to the library? I can handle with this modification.

`andThen` is not a lawful Monad

After doing some work porting this to Haskell and I discovered that there is an inconsistency between andMap and andThen.

Specifically it breaks the ap law:

ap mf ma = mf >>= (\f -> ma >>= (\a -> return (f a)))
  
ap == (<*>)

or in an Elm context

ap ma mf = andThen (\a -> andThen (\f -> Success (f a)) mf) ma

ap == andMap

But this is not the case, given this counter example:

ap     NotAsked Loading = NotAsked
andMap NotAsked Loading = Loading

What you have right now for andMap is a valid Applicative, but the specific Applicative it is does not lead to Monad. The correct Applicative to match andThen would be as follows:

andMap : RemoteData e a -> RemoteData e (a -> b) -> RemoteData e b
andMap wrappedValue wrappedFunction =
    case ( wrappedFunction, wrappedValue ) of
        ( Success f, Success value ) ->
            Success (f value)
            
        ( _, Failure error ) ->
            Failure error
            
        ( _, Loading ) ->
            Loading

        ( _, NotAsked ) ->
            NotAsked
            
        ( Failure error, _ ) ->
            Failure error

        ( Loading, _ ) ->
            Loading

        ( NotAsked, _ ) ->
            NotAsked

Now the counter example is lawful

ap     NotAsked Loading = NotAsked
andMap NotAsked Loading = NotAsked

Impossible states in update with RemoteData

Let's say for example that we are doing request with optimistic update. In this case our update function might look as follows:

type Msg = SomethingLoaded Something (RemoteData.RemoteData Http.Error Something)

update msg model =
    case msg of
        SomethingLoaded oldSomething webData ->
            case webData of
                RemoteData.NotAsked ->
                    -- this is impossible since after request is finished RemoteData is either Failure or Success

                RemoteData.Loading ->
                    -- this is impossible since after request is finished RemoteData is either Failure or Success

                RemoteData.Failure _ ->
                    -- here I want to restore value from oldSomething

                RemoteData.Success ->
                    -- here I want to put the proper value in place

One might argue here that wildcard could be used to return (model, Cmd.none) in such cases. That is true, however it's moves a person who is looking at this code for the first time into dynamic programming domain. As opposed to static programming it requires him to keep in his brain what and why about this scenario.

Also that goes a bit against popular mantra in Elm community of making impossible states impossible.

The way it could be solved is:

type RemoteData e a
    = NotAsked
    | Loading
    | LoadedData (Loaded e a)

type Loaded e a = Failure e | Success a

Then example will be turned into:

type Msg = SomethingLoaded Something (RemoteData.Loaded Http.Error Something)

update msg model =
    case msg of
        SomethingLoaded oldSomething loaded ->
            case loaded of
                RemoteData.Failure _ ->
                    -- here I want to restore value from oldSomething

                RemoteData.Success ->
                    -- here I want to put the proper value in place

Thoughts?

mapN functions, andMap

Thanks for doing this library, I picked it up today and found it immediately useful.

  1. Would you consider adding convenience functions map2, map3, etc. ? Otherwise you end up either importing <*> which seems problematic if that operator is defined elsewhere, or doing verbose chains like RemoteData.pure fn |> (flip RemoteData.apply) rd1 |> (flip RemoteData.apply) rd2 . (Granted that will be less annoying in 0.18.)
  2. Should apply be replaced with the quasi-Elm-standard andMap ? Or aliased?

Let me know, I would be glad to submit a PR.

Consider use of Monoids

Not an Elm programmer, but I think there is a chance to use monoids to get something similar (but with a homogeneous type structure) to append

and also on the non-result type to collect traces

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.