FCards - On the Web

23. Making our site dynamic

Model-View-Update (MVU)

In order to interact with people Bolero uses a MVU system:

Add interactivity to our game

Put it in the page

First of all we need to add a place on the webpage for our “app” to run, and we need to include the standard blazor javascript library to hook it up.

To do this, update the index.html file to:

<html>
  <head>
    <title>Solitaire</title>
  </head>
  <body>
    <h1>Solitaire</h1>
    <div id="main">Loading...</div>

    <script src="_framework/blazor.webassembly.js"></script>    
  </body>
</html>

Add the MVU cycle

Then, we add a new file for our main app called “Main.fs” (and don’t forget to add it to the project file above the Startup.fs entry).

module Solitaire.Website.Main

open Elmish
open Bolero
open Bolero.Html

type MyApp() =
  inherit ProgramComponent<SimpleModel, SimpleMessage>()

  override this.Program =
    Program.mkSimple initialise update view

From this code you can see that we need to create a few things for the standard bolero code to work.

We need a couple of types for our model and list of messages:

type SimpleModel = string

type SimpleMessage =  // must be a DU
  | DoNothing

We also need the functions that Program.mkSimple use to make the MVU cycle work

  1. Initialise the model (of type SimpleModel) at the start, provided some startup arguments (which we’ll ignore for now)
      let initialise args = ""   
    
  2. An updater function that takes a message and the current model and returns the updated model. We’re not even doing anything with the message yet.
      let update message model = model   
    
  3. A view function that takes a model and a dispatcher (more on that later) and returns a webpage Node like some text, or a heading, or maybe an image.
    I used the simple text function that turns a string into a simple piece of text on a webpage
      let view model dispatch = text "Let's play Solitaire!" 
    

Include the app in the startup

To include the app, we add the following line into the Startup.fs before the builder executes it’s Build() function.

builder.RootComponents.Add<Main.MyApp>("#main")

See the View

That’s it. Run the project again and now you should see the “Loading…” text pop up for a second before it’s replaced by our view text

Code so far

Website/Startup.fs

namespace Solitaire.Website

open System
open System.Net.Http
open Microsoft.AspNetCore.Components.WebAssembly.Hosting
open Microsoft.Extensions.DependencyInjection

module Program =

  [<EntryPoint>]
  let Main args =
    let builder = WebAssemblyHostBuilder.CreateDefault(args)
    builder.Services.AddScoped<HttpClient>(
      fun _ -> new HttpClient(BaseAddress = Uri builder.HostEnvironment.BaseAddress)
      ) 
      |> ignore
    builder.RootComponents.Add<Main.MyApp>("#main")
    builder.Build().RunAsync() |> ignore
    0

Website/wwwroot/index.html

<html>
  <head>
    <title>Solitaire</title>
  </head>
  <body>
    <h1>Solitaire</h1>
    <div id="main">Loading...</div>

    <script src="_framework/blazor.webassembly.js"></script>    
  </body>
</html>

Website/Main.fs

module Solitaire.Website.Main

open Elmish
open Bolero
open Bolero.Html

type SimpleModel = string

type SimpleMessage =  // must be a DU
  | DoNothing

let initialise args = ""

let update message model = model

let view model dispatch = text "Let's play Solitaire!" 

type MyApp() =
  inherit ProgramComponent<SimpleModel, SimpleMessage>()

  override this.Program =
    Program.mkSimple initialise update view