Elm save app state

So I have a task to cache everything that happens on a page in the browser local storage, I have created the outgoing port but I am finding it difficult to send the whole model through to javascript, I need to cache send the whole state of the app real-time, I used onInput function.

Be more specific. What is different about it?

Hi Victory,
Iā€™m not too sure where youā€™re running into difficulty. But the general approach would be to use the Json.Encode library to transform your appā€™s Model into a value of type Json.Encode.Value.

The port should be defined as below. Actually I copied this from the ā€œportsā€ page of the Elm guide, which might be worth reviewing. The example there is based around localStorage!

port module Main exposing (..)

import Json.Encode as E

port cache : E.Value -> Cmd msg
2 Likes

If your Model structure is made of simple Elm types (Int, Float, String, Bool or records/tuples/lists of them), you can send the model to JS and write it to localstorage. You will then be able to reload it in init from flags. elm-todomvc is an example on how to do this.

If your Model contains a custom type, you will need to create an encoder and a decoder for the Model and send Value through the ports.

1 Like

My model is like an Shopping checkout page, It contains Input fields mostly

Hi Victory,

The more specifics you provide, the easier it will be for us to help you. Code snippets/examples are helpful along with any error messages that you may be encountering.

That said, Iā€™ll have a go based on your two posts :slight_smile:

From what youā€™ve said so far, Iā€™m assuming that what you need to do is to update the cache/localStorage each time an input box is updated/changed by the user(?) so that if they should close the page halfway through, they can restore the previous state and continue from where they left off. If this is the case, you probably wonā€™t need to send your whole model through in one go. You can just send the individual values each time they change - the user can only fill in one field at a time, so that should be all you need to worry about.

You havenā€™t mentioned anything about the rest of your model, such as what types might be involved etc, so I will only address the input boxes changing through user input.

The values in the input boxes will come back to your update function as Strings so as pdamoc already mentioned, they can be sent straight through a port to JS without having to worry about decoders.

This gives you a couple of options.

  1. You could set up a port for each input field, and send each one through to JS as they change. You would then need corresponding ā€˜listenersā€™ on the JS side to receive the data from each port, or
  2. You could use a single port, which is probably the better option. In this case, you would also need to send an identifier through along with the value you wish to store, so that your JS code knows which field is being cached. This way you would just need one ā€˜listenerā€™ on the JS side that would store the data according to the identifier you send through with the value.

So, for option 2, you could have something like this:

type alias Model =
  { field1 : String
  , field2 : String
  }

type Msg
  = Field1Changed String
  | Field2Changed String

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    Field1Changed val ->
      ( { model | field1 = val }
      , cacheField {id = 1, value = val}
      )

    Field2Changed val ->
      ( { model | field2 = val }
      , cacheField {id = 2, value = val}
      )


port cacheField : {id : Int, value : String} -> Cmd msg

Hopefully, that points you in the right direction.

For the JS side, if you havenā€™t already, it might be worth having a look at: https://github.com/macmcmeans/localDataStorage which enables storing and retrieving whole objects to and from localStorage.

HTH

Exactly it contains Custom types, So now the issue is creating the encoder and decoder for the model and sending the value through the port, Once i do this, the issue is solvedā€¦
I have created the incoming and outgoing port already:

port module Main exposing (ā€¦)

import Browser
import Html exposing (Html, a, br, button, div, form, h1, i, img, input, label, option, section, select, small, span, strong, text)
import Html.Attributes exposing (class, disabled, for, href, id, name, placeholder, src, style, type_, value)
import Html.Events
import Http
import Json.Decode as D
import Json.Decode.Pipeline as Pipeline
import Json.Encode as E
import List.Extra
import String

port incoming : (E.Value -> msg) -> Sub msg

port outgoing : OutgoingMsg -> Cmd msg

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.