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 the
Not Asked state because we haven’t asked for that data yet. When the button is clicked the request transitions to the
Loading state. If the request is successful, it ends up in the
Success state. If not, it moves to the
So far we have only dealt with the
Failure states. Once we handle the remaining two, our UI will be in a much better shape. 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 update
elm.json. After that, import the
RemoteData module in
Handling Not Asked State
RemoteData module provides a type by the same name.
Aha! The four data constructors look identical to different states we saw earlier. 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
RemoteData. Go ahead and change the
Model type in
DecodingJson.elm to this:
We removed the
errorMessage field because any potential error now resides in the
posts field itself. We can simplify the type of
posts by using
WebData is defined in the
RemoteData module as a type alias.
WebData represents data fetched from an HTTP (also known as Web) server like ours. That’s why the error type is hard-coded to
Http.Error. If we were retrieving data from a non-HTTP server such as FTP, we would have to use the
RemoteData type instead of
WebData. All non-HTTP requests also go through the same four states we covered earlier.
Step 2: Modify init
Currently, we initialize the
posts field to an empty list.
It 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 the 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. Let’s replace
RemoteData.NotAsked and remove
Step 3: Modify DataReceived’s Payload Type
Next we need to replace the
Result type in
DataReceived message’s payload with
Step 4: Convert Result to RemoteData Value
Here is how we’re creating a command for fetching posts right now:
The type of the
expect field in
Http.get depends on the type of our
Before we introduced
DataReceived had the following type.
DataReceived’s type has changed to this:
WebData is just a type alias for
RemoteData, the above type signature is equivalent to this:
This means the
Result type must be converted to
RemoteData.fromResult function is just what we need. Here is how its type signature looks:
DecodingJson.elm to this:
As mentioned in the Using » Operator section in chapter 5,
>> is a built-in operator for composing multiple functions. Here is an example:
So conceptually we can think of
RemoteData.fromResult >> DataReceived as:
Remember, all messages that contain a payload are essentially functions behind the scenes. That’s why we were able to use
Step 5: Modify update
DataReceived’s payload is now of type
WebData instead of
Result. That means we can replace the
DataReceived (Ok posts) -> and
DataReceived (Err httpError) -> branches in
update with something much simpler.
All we’re doing in the
DataReceived response -> branch is assign whatever
response we get to the
posts field in our model.
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. We’re ready to test our app. 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/src/DecodingJson.elm. The only element you should see is a button.
Transitioning to the Loading State
json-server from the
beginning-elm directory in terminal using the following command if it’s not running already.
Click the Get data from server button and you should immediately see a table containing posts. 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 a second before responding to all requests. Refresh the page at
http://localhost:8000/src/DecodingJson.elm and click the Get data from server button.
Hmm… We’re still not seeing
Loading.... We forgot to change the state from
update before firing off the HTTP command.
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/src/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/src/DecodingJson.elm and click the Get data from server button. You should see the following error message.
Don’t forget to change the URL back to
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 at 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: