In the previous section, we created an app whose view looked like this before clicking the Get data from server button:
It’s not considered a good UI practice to show the Posts heading and table headers when we haven’t even fetched any data yet. We need to hide those until the data is retrieved. Before we do that though let’s look at different states an HTTP request tends to be in.
Before the Get data from server button is clicked the request for fetching posts is in Not Asked state because we haven’t asked for data yet. When the button is clicked the request transitions to Loading state. If the request is successful, it ends up in Success state. If not, it moves to Failure state.
So far we have only dealt with the Success and Failure states. Once we handle the remaining two, our UI will be in a much better place. We will be using a third-party package called
krisajenkins/remotedata to handle the remaining states. Go ahead and install it by running the following command from the
beginning-elm directory in terminal.
y when asked to add the package as a dependency to
elm-package.json and approve the installation plan. After that, import the
RemoteData module in
Handling Not Asked State
RemoteData provides a type called
WebData which is defined like this:
It’s just an alias for another concrete type called
RemoteData which has the same name as the module.
Here is what the
RemoteData type’s definition looks like:
Aha! The four data constructors above look identical to the various states we saw earlier. If we were fetching data from a non-HTTP server such as FTP, the request would still go through the same four states. Therefore, the
RemoteData type can be used with non-HTTP requests as well.
Elm apps mostly retrieve data from an HTTP (also known as Web) server. That’s why the
RemoteData module has hardcoded the error type to
WebData exists specifically to represent data retrieved by an HTTP request, so we’ll be using it instead of the
RemoteData type throughout this section.
We need to modify several parts of our app to be able to take advantage of
RemoteData. What follows is a step-by-step guide for making those changes.
Step 1: Modify the Model
The first thing we need to do is change our model. Currently, we are directly assigning a list to the
We need to wrap that list with
WebData. Go ahead and change the
Model type in
DecodingJson.elm to this:
errorMessage field was also removed. We don’t need to store an error message in our model anymore. More on this later.
Step 2: Modify
Currently, we initialize the
posts field to an empty list.
It actually doesn’t make sense to assign an empty list to
posts from the get go. What if the server responds with an empty list indicating there are genuinely no posts in database? How do we differentiate between that scenario and not having requested data in the first place? The answer is to use
NotAsked instead of an empty list. Update
DecodingJson.elm like this:
Step 3: Modify
DataReceived’s Payload Type
Next we need to replace the
Result type in
DataReceived message’s payload with
Step 4: Use
sendRequest to Create a Command
Right now we’re using the
Http.send function to generate a command.
RemoteData requires us to use a different function called
sendRequest. In addition to creating an HTTP command, the
sendRequest function also wraps the response in
WebData type. Let’s use it in
Notice how we moved
DataReceived to the next line and passed it as the first argument to
sendRequest function doesn’t accept a message as one of its arguments.
If it doesn’t take a message then how will the
update function be notified after the command is executed? The answer to that question lies in
sendRequest’s return type:
Cmd (WebData a). It hardcodes the message type as
Wait a minute. Can
sendRequest use a type alias (
WebData) as a message? It totally can. The term message by itself doesn’t have any significance in Elm. Any data type can play the role of message. We’ve been calling the data constructors in
Msg type messages because it’s easier to understand the interaction between the Elm Runtime and our application if we think of them as two entities communicating with each other by sending messages.
Step 5: Modify the
When a command generated by
sendRequest is executed, the
update function is sent a message of type
WebData a. Usually, we would handle that message like this:
But we can’t really do that.
update takes a message of type
Msg, whereas the message sent to it is of type
WebData a. To resolve this, we can map
WebData a to
Msg by using the
Here is what
Cmd.map’s type signature looks like:
Now we can simply assign the payload in
DataReceived message, which is of type
WebData a, to the
posts field in our model. Modify the
update function to this:
Previously, we had two branches for handling the
The first branch was for extracting a list of posts from a
Result type before assigning them to our model. The second branch was for extracting an error. With
WebData all of this extraction will happen in view code.
Step 6: Modify View Code
The only remaining change is to handle different states in our view code. Modify the
viewPostsOrError function in
DecodingJson.elm to this:
Previously, we were checking for the presence of an error message to determine whether to display posts or an error view.
Now we’re determining which view to display based on different states defined in the
RemoteData type. Run
elm-reactor from the
beginning-elm directory in terminal if it’s not running already and go to this URL in your browser:
http://localhost:8000/elm-examples/DecodingJson.elm. The only element you should see is a button.
Transitioning to the Loading State
You’ll see a table containing posts immediately after clicking the Get data from server button. Although the HTTP request transitions to Loading state after the button is clicked, we don’t see
Loading... on the page. For that text to appear, we need to make our server wait a couple of seconds before returning a response. Stop
json-server by pressing
Ctrl + c and restart it with the
--delay takes the number of milliseconds as an argument.
json-server will now wait for two seconds before responding to all requests. Refresh the page at
http://localhost:8000/elm-examples/DecodingJson.elm and click the Get data from server button.
Hmm… We’re still not seeing
Loading.... We forgot to change the state from
Loading before firing off the HTTP command to the Elm Runtime.
RemoteData doesn’t transition to Loading automatically. We need to do that manually inside the
SendHttpRequest -> branch in
Refresh the page at
http://localhost:8000/elm-examples/DecodingJson.elm one more time and click the Get data from server button. You should now see
Loading... while the posts are being fetched.
Transitioning to the Failure State
Change the URL inside
DecodingJson.elm to something invalid.
Since we’re requesting a non-existent resource, the HTTP request will eventually transition to the Failure state. Refresh the page at
http://localhost:8000/elm-examples/HttpExamples.elm and click the Get data from server button. You should see the following error message after the loading text has disappeared.
Don’t forget to change the URL back to “http://localhost:5019/posts” in
In this section, we used a third-party package called
krisajenkins/remotedata to improve our UI by properly handling all four states an HTTP request can be in any given time. Those four states are:
Failure. Kris Jenkins — the author of that package — has written a wonderful blog post explaining the rationale behind creating
RemoteData. I highly recommend you read it.
In the next section, we will learn how to send an HTTP command when the app is being initialized. Here is the entire code from
DecodingJson.elm thus far: