Active pages vs passive pages

In my Browser.application app I have a page of text input boxes allowing the user to edit the information in each.

Necessarily, the text in each box must be a copy of data from the main data structure, so that the user can hit the back button and discard all changes, or hit the save button and save the changes back into the main data structure (which is held in Shared.elm).

I can see two ways to do this:

  1. Make the page a passive page, and simply store the copied data in an edit record in Shared.elm and manipulate it via messages sent to Main.elm.

  2. Make the page an active page. That is, give it a function
    page ... = Browser.sandbox {...}, so that the copied data is held in the local model and manipulated via the local update function.

My questions:

  1. How do I make the page function execute?

  2. In an active page, how do I separate messages that are only for internal consumption from messages that must be converted using Element.map (elm-ui equivalent to Html.map) and handled within Main.elm?

  3. Of the two approaches (assuming there aren’t more) which do you favour?
    If I go entirely for one approach or another, what are the pain points down the line?

Note that it’s not possible to do page = Browser.sandbox { ... } and have it execute within a Browser.application. Both of those return a Program. The only thing one can do with a Program is to assign it to main and have the Elm compiler wire it up for execution via JavaScript.

1 Like

Thank you, Simon (@lydell)

When I played with elm-spa and created a page, the page contained:

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

How does that work?

Is it possible to create a page within an application with it’s own model, update, view, etc?

That’s another function, also called sandbox. The Page.sandbox docs say that it’s “inspired by Browser.sandbox”. It does not return a Program – it returns a Page.With, which is some kind of internal data structure that elm-spa eventually wires up for you – see below.

Yes! I’d say it’s pretty common to have pages with their own model, update, view. (You might hear and read advice to avoid having sub-model, update, view, and while that’s many times good, it’s definitely the right tool for pages.)

Your main Elm file becomes a Russian Doll kind of routing thing. The main Elm file’s Model contains your page’s Model, the main file’s update sometimes calls the page’s update function, and the main file’s view sometimes calls the page’s view function. Use Html.map and Cmd.map to make the types line up when the page has its own Msg type. See the elm-spa-example for an example.

So in Elm, you can have nested “applications” with their own model, update, view etc. but in the end it’s just functions and types that you wire up manually. The wiring might feel difficult and tedious the first time, but on the upside it makes it easy to jump into somebody else’s project – you can basically see how the whole project fits together only by using “go to definition” and “find references” in your IDE. The manual wiring is also useful when you need to do something custom – since there’s no “magic” wiring things up, it’s just code like any other where you can do whatever you want.

elm-spa helps with some of that wiring. But the cool thing is that you can still jump into the code it generates and see how it all fits together.

2 Likes

Simon, many thanks.

That’s a very nice explanation that all makes sense. I think I now have the final piece of the puzzle to make the transition from Browser.element to Browser.application :slight_smile:

Everything you’ve said I should have found out for myself by doing enough research. So thank you for ignoring my failure to do that and taking pity on me.

1 Like

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