In chapter 6 we built an app that allowed us to create, read, update, and delete posts from an HTTP server.
What happens to the app when the server is unreachable? Let’s find out. Run the following command from the
beginning-elm directory in terminal to launch the app.
Also start an HTTP server by running the following command from the
beginning-elm directory in a separate terminal window.
Now go to
http://localhost:8000/ in a browser. You should see two posts.
json-server by pressing
Ctrl + c. After that reload the page at
http://localhost:8000/. You should now see an error.
It would be great if we could display the posts from the previous fetch as soon as the app loads even if the server is unreachable. As it turns out, all client-side web applications can in fact store data locally in a browser through the use of a technology called Web Storage which provides two different ways to save data:
Session storage - Data stored in session storage is available until the browser is closed.
Local storage - Data stored in local storage is available even if the browser is closed and reopened.
Saving State in Local Storage
Ports.elm in the
beginning-elm/PostApp directory and add the code below to it.
We have created an outgoing port function called
storePosts which takes a list of posts as an input and returns a command. In the Model View Update - Part 1 section, we learned that the state of an app is typically represented by a model. So why are we storing only the posts and not everything inside our model?
Not everything inside the model is needed to bring the app back to a usable state. In our case, all we need is a list of posts. That’s why we aren’t storing the
posts field in our model actually contains the
WebData type. We need to somehow extract the actual posts stored inside
WebData. We’ll do that in a new function called
updateWithStorage. Let’s add it right above the
update function in
We’re doing more than just extracting posts in the above function. Our strategy here is to have the Elm runtime send all messages to
updateWithStorage instead of
update. Why do we want to do that?
update function returns a new model, we want to extract the posts out and save them in local storage. Rather than creating a command for doing that in every single branch of
update, we created a wrapper function that uses
Cmd.batch to append the command for storing posts to the list returned by
Let’s go over the code that extracts posts from the new model real quick to make sure that you understand how it works.
What we’re doing here is convert the
WebData type stored in the
posts field to a
Maybe. After that we can use the
withDefault function to pull the posts out. The posts are actually stored as a payload inside the
Success data constructor.
If you don’t remember how the
WebData type works, you might want to read the RemoteData section from chapter 6 once again.
If the value inside the
posts field is anything other than
Nothing in which case we assign an empty list to the
We need to import the
Ports module in
State.elm. That’s where the
storePosts outgoing port function is defined.
Now let’s replace
We also need to expose
That’s all we need to do on the Elm side. Check the
elm-live window in terminal to make sure there are no errors.
Compiling Our Code to
index.html in the
beginning-elm directory if it doesn’t exist already. and replace its contents with the code below.
All we’re doing here is load the
elm.js file and embed the Elm app in a
div. Next we need to put the compiled Elm code in
elm-live if it’s running already by pressing
Ctrl + c and restart it by running the following command from the
beginning-elm directory in terminal.
--output option tells
elm-live to compile the code in
App.elm and put it in
elm.js. If we don’t use the
elm-live will automatically create a file called
index.html in the directory from where it’s run and put the compiled code in that file instead.
In the previous sections of this chapter we used
elm-make to produce
elm.js like this:
Why couldn’t we use
elm-make here too? That’s because
PostApp implements routing and we need the
pushstate feature from
elm-live to be able to navigate between pages.
Replace the code inside the last
<script> tag in
beginning-elm/index.html with the following code.
We’re now listening to the
storePosts outgoing port defined in the Elm app.
For now, we’re simply printing the data coming through the port in the browser console. We need to first make sure that we’re actually receiving the posts sent by the Elm app before we save them in local storage.
json-server back up by running the following command from the
beginning-elm directory in terminal.
http://localhost:8000/ in a browser and open the browser console. You should see the posts in a raw JSON string format.
Now update the callback function in
beginning-elm/index.html to actually save the posts in local storage.
We’re using the
setItem method in
localStorage to save the posts. That method takes a key value pair.
post-app-save is the key and the raw JSON string produced by the
JSON.stringify method is the value.
If you are using Chrome, you can check whether or not the data was indeed saved in local storage by opening the developer tools and going to the Application tab as shown in the figure below.
Before we move on, we need to fix routing in
PostApp. Right now if you click the Create new post link, you’ll be taken to
http://localhost:8000/posts/new, but that page is blank.
If you open the browser console, you’ll see
404 (Not Found) error. You’ll also see the
Elm is not defined error.
Where are these errors coming from? Let’s see what’s going on in the terminal.
elm-live trying to load the path
/posts/elm.js instead of
/posts/new? The reason for that is
elm-live needs to serve the entire app — contained in
elm.js — regardless of which path is being loaded for routing to work properly. That’s why you see a redirect to
/elm.js when we went to the home page whose path is
How come we didn’t receive an error when we went to the home page? That’s because
elm.js from the root directory.
elm-live in fact did put the
elm.js file in our root directory (
beginning-elm) — the directory from which we ran the
elm-live command earlier.
But when we try to go to the
/posts/new path, instead of serving
elm.js from the root directory again,
elm-live decides to look for it in the
/posts directory which doesn’t exist. If
elm.js isn’t loaded then the Elm app can’t be initialized. That’s why that error
Elm is not defined, we saw earlier, was displayed in the browser console.
How do we fix it? Believe it or not all we need to do is add a forward slash in front of
elm.js in the script tag that loads it. Make that change in
elm-live will serve
elm.js from the root directory no matter which path we go to. Now if you click the Create new post from
http://localhost:8000/, you should see the contents of the page for creating a new post.