# Storage

__Source code__: [GitHub](https://github.com/ryan-haskell/elm-spa/tree/main/examples/03-local-storage)

Let's start by creating a new project with the __elm-spa__ CLI:

```terminal
elm-spa new
```

## Creating a stateful page

Let's create a simple interactive app, based on the official Elm [counter example](https://elm-lang.org/examples/buttons). The `elm-spa add` command will make this a breeze:

```terminal
elm-spa add / sandbox
```

This will stub out the `init`, `update`, and `view` function for us, and wire them together with `Page.sandbox` like this:

```elm
-- src/Pages/Home_.elm

page : Shared.Model -> Request -> Page.With Model Msg
page =
    Page.sandbox
        { init = init
        , update = update
        , view = view
        }
```

Let's add in the implementation from the counter example to get a working app!


### init

```elm
-- src/Pages/Home_.elm

type alias Model =
    { counter : Int
    }

init : Model
init =
    { counter = 0
    }
```

### update

```elm
-- src/Pages/Home_.elm

type Msg = Increment | Decrement

update : Msg -> Model -> Model
update msg model =
    case msg of
        Increment ->
            { model | counter = model.counter + 1 }

        Decrement ->
            { model | counter = model.counter - 1 }
```

### view

```elm
-- src/Pages/Home_.elm

view : Model -> View Msg
view model =
    { title = "Homepage"
    , body =
        [ Html.h1 [] [ Html.text "Local storage" ]
        , Html.button [ Html.Events.onClick Increment ] [ Html.text "+" ]
        , Html.p [] [ Html.text ("Count: " ++ String.fromInt model.counter) ]
        , Html.button [ Html.Events.onClick Decrement ] [ Html.text "-" ]
        ]
    }
```

After these functions are in place, we can spin up our server with the __elm-spa__ CLI:

```terminal
elm-spa server
```

And this is what we should see at [http://localhost:1234](http://localhost:1234):

![counter app](/content/images/03-storage.png)

### Playing with the counter

As we click the "+" and "-" buttons, the counter value should be working great. When we __refresh__ the page, the counter value is 0 again.

Let's use local storage to keep the counter value around!

## The JS side

To do this, we'll be using [flags and ports](https://guide.elm-lang.org/interop/ports.html), a typesafe way to work with JavaScript without causing runtime errors in our Elm application!

Let's edit `public/index.html` as a starting point:

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
  <script src="/dist/elm.js"></script>
  
  <!-- EDIT THIS LINE -->
  <script src="/main.js"></script>

</body>
</html>
```

Here we replace the inline `Elm.Main.init()` script generated by the `elm-spa new` command with a reference to a new file we'll create in `public/main.js`

```js
//  public/main.js

const app = Elm.Main.init()

// ...
```

At this point, nothing has changed yet, but we now have access to `app`– which will allow us to interact with our Elm app from the JS file!

Let's add in some ports like this:

```js
//  public/main.js

const app = Elm.Main.init({
  flags: JSON.parse(localStorage.getItem('storage'))
})

app.ports.save.subscribe(storage => {
  localStorage.setItem('storage', JSON.stringify(storage))
  app.ports.load.send(storage)
})
```

This JS code is doing a few things:

1. __When our Elm app starts up,__ we pass in the current value of `localStorage` via flags. Initially, this will pass in `null`, because no data has been stored yet.

2. We subscribe to the `save` port for events __from Elm__, which we'll wire up on the Elm side shortly.

3. When Elm sends a `save` event, we'll store the data in localStorage (making it ready for the next time the app starts up!) as well as send a message back to Elm via the `load` port.

## The Elm side

None of this code is working yet, because we need to define these `save` and `load` ports on the Elm side too!

Let's create a new file at `src/Storage.elm` that defines the ports referenced on the JS side:

```elm
port module Storage exposing (..)

import Json.Decode as Json

port save : Json.Value -> Cmd msg

port load : (Json.Value -> msg) -> Sub msg
```

Above, we've created a `port module` that defines our `save` and `load` ports. Next, we'll describe the data we want to store, as well as how to convert it to and from JSON:

```elm
port module Storage exposing
    ( Storage, fromJson, onChange
    , increment, decrement
    )

import Json.Encode as Encode

-- ... port definitions from before ...

type alias Storage =
  { counter : Int
  }


-- Converting to JSON

toJson : Storage -> Json.Value
toJson storage =
    Encode.object
        [ ( "counter", Encode.int storage.counter )
        ]


-- Converting from JSON

fromJson : Json.Value -> Storage
fromJson value =
    value
        |> Json.decodeValue decoder
        |> Result.withDefault initial

decoder : Json.Decoder Storage
decoder =
    Json.map Storage
        (Json.field "counter" Json.int)

initial : Storage
initial =
  { counter = 0
  }
```

If this decoder stuff is new to you, please check out the [JSON section of the Elm guide](https://guide.elm-lang.org/effects/json.html). It will lay a solid foundation for understanding decoders and encode functions!

### Sending data to JS

For this example, we're going to define `increment` and `decrement` as side-effects because they change the state of the world. We'll be using the `save` port to send these events to JS:

```elm
-- src/Storage.elm

increment : Storage -> Cmd msg
increment storage =
    { storage | counter = storage.counter + 1 }
        |> toJson
        |> save

decrement : Storage -> Cmd msg
decrement storage =
    { storage | counter = storage.counter - 1 }
        |> toJson
        |> save
```

This should look pretty similar to how our homepage handled the `Increment` and `Decrement` messages, but this time we use `toJson` and `save` to send an event for JS to handle.

( As a last step, we'll revisit `Home_.elm` and swap out the old behavior with the new )

### Listening for data from JS

We're going to add one final function to `Storage.elm` that will allow us to subscribe to events from the `load` port, that use's our `fromJson` function to safely parse the message we get back:

```elm
onChange : (Storage -> msg) -> Sub msg
onChange fromStorage =
    load (\json -> fromJson json |> fromStorage)
```

Here, the `onChange` function will allow the outside world to handle the `load` event without having to deal with raw JSON values by hand. 

That's it for this file- now we're ready to use our `Storage` module in our app!

### Wiring up the shared state

Let's eject `Shared.elm` by moving it from `.elm-spa/defaults` into our `src` folder. This will allow us to make local changes to it, as explained in the [shared state section](/guide/05-shared-state) of the guide.

Our first step is to add `Storage` to our `Shared.Model`, so we can access `storage` from _any_ page in our application:

```elm
-- src/Shared.elm

import Storage

type alias Model =
    { storage : Storage
    }
```

The `Shared.init` function is the __only place__ we have access to `Flags`, which is how JS passed in our initial value earlier. We can use `Storage.fromJson` to convert that raw JSON into our nice `Storage` type.

```elm
-- src/Shared.elm

init : Request -> Flags -> ( Model, Cmd Msg )
init _ flags =
    ( { storage = Storage.fromJson flags }
    , Cmd.none
    )
```

Now let's listen for those `load` events from JS, so we can update the `Shared.Model` as soon as we get them. This code will use the `Storage.onChange` function we made to send a `Shared.Msg` to our `Shared.update` function:

```elm
-- src/Shared.elm

subscriptions : Request -> Model -> Sub Msg
subscriptions _ _ =
    Storage.onChange StorageUpdated


type Msg
    = StorageUpdated Storage

update : Request -> Msg -> Model -> ( Model, Cmd Msg )
update _ msg model =
    case msg of
        StorageUpdated storage ->
            ( { model | storage = storage }
            , Cmd.none
            )
```

That's all for `src/Shared.elm`. The last step is to upgrade our homepage to send side-effects instead of changing the data locally.

### Upgrading Home_.elm

To gain access to `Cmd msg`, we'll start by using `Page.element` instead of `Page.sandbox`. The signature of our `init` and `update` functions will need to change to handle the new capabilities:

Our `Model` no longer needs to track the state of the application. This means the `Home_.init` function won't be doing much at all:

```elm
-- src/Pages/Home_.elm

type alias Model =
    {}

init : ( Model, Cmd Msg )
init =
    ( {}, Cmd.none )
```

This time around, the `update` function will need access to the current `Storage` value and use `Storage.increment` and `Storage.decrement` to send commands to the JS side.


```elm
-- src/Pages/Home_.elm

type Msg
    = Increment
    | Decrement

update : Storage -> Msg -> Model -> ( Model, Cmd Msg )
update storage msg model =
    case msg of
        Increment ->
            ( model
            , Storage.increment storage
            )

        Decrement ->
            ( model
            , Storage.decrement storage
            )
```

When the `load` event comes in from JS, it triggers our `Storage.onChange` subscription. This updates the `storage` for us, meaning the `storage.counter` we get in our `view` will be the latest counter value.

```elm
-- src/Pages/Home_.elm

view : Storage -> Model -> View Msg
view storage _ =
    { title = "Homepage"
    , body =
        [ Html.h1 [] [ Html.text "Local storage" ]
        , Html.button [ Html.Events.onClick Increment ] [ Html.text "+" ]
        , Html.p [] [ Html.text ("Count: " ++ String.fromInt storage.counter) ]
        , Html.button [ Html.Events.onClick Decrement ] [ Html.text "-" ]
        ]
    }
```

We can use `Page.element` to wire all these things up, and even pass `Storage` into our `view` and `update` functions, which depend on the current value to do their thing:


```elm
-- src/Pages/Home_.elm

page : Shared.Model -> Request -> Page.With Model Msg
page shared _ =
    Page.element
        { init = init
        , update = update shared.storage
        , view = view shared.storage
        , subscriptions = \_ -> Sub.none
        }
```

> Here, I've stubbed out `subscriptions` with an inline function, we won't be needing it, because Shared.subscriptions listens to `Storage.onChange` for us.


#### Hooray!

In the browser, we now have a working counter app that persists on refresh. Even if you close the browser and open it up again, you'll see your previous counter value on the screen.

As a reminder, all the source code for this example is available on [GitHub](https://github.com/ryan-haskell/elm-spa/tree/main/examples/03-local-storage)

---

__Next up:__ [User Authentication](./04-authentication)
