Building a Loading Icon Formlet Combinator

Joel Bjornson

Joel Bjornson

May 18, 2011

Reading time:

4 mins

Share via:

In my previous post I show how to create custom formlets using Formlet.BuildFormlet. Continuing on the same theme, here is an example of how to create a custom formlet combinator for capturing the common AJAX pattern of submitting data to the server and displaying a loading icon while awaiting the result. The scenario to be addressed is:

  • A client side formlet produces a value.
  • The value is posted to the server and a loading icon is displayed.
  • The server returns and the loading panel is removed.
  • Depending on the result of the returned value, the continuation of the form is displayed.

Prefarably the interface should be non-blocking. This is acchieved by making RPC server side function return async values.

Since the server call can be represented as an arbitrary asynchronous computiation, we can simply define a combinator that lifts an asyncrounous value into a formlet, displaying a loading panel until the computation is done:

[<JavaScript>]
let LoadingFormlet (a:Async<'T>) : Formlet<'T> =
    let loadingPane =
        Formlet.BuildFormlet <| fun _ ->
            let elem = Div [Attr.Class "loadingPane"]
            let state = new Event<_>()
            async {
                let! value = a
                do state.Trigger (Result.Success value)
                return ()
            }
            |> Async.Start
            elem, ignore, state.Publish    
    Formlet.Replace loadingPane ( fun value -> 
        Formlet.Empty () 
        |>  Formlet.InitWith value
    )

The loadingPane formlet is constructed as an custom formlet, using Formlet.BuildFormlet. The body part is a div element with a special class attribute to allow styling of the panel. The state is an event that is triggered as soon as the async computation produces a value.

The function Formlet.Replace is used to create a formlet that replaces the loading panel with an empty formlet once the value is triggered.

Using the combinator is straightforward. Here is an example:

[<JavaScript>]
let MyForm =
    Formlet.Do {
        // Get the value of the form
        let! message = 
            Controls.TextArea ""
            |> Enhance.WithTextLabel "Message"
            |> Enhance.WithLabelAbove
            |> Enhance.WithSubmitButton

        // Call the server
        let! res = LoadingFormlet (Post message)
        
        // Display the server result 
        return! 
            Formlet.OfElement (fun _ ->
                Div [Attr.Class "info"] -< [Text res]
            )
    }        
    |> Enhance.WithFormContainer

And the resulting form:

Review note
This resource needs to be re-added.

After submitting the form the loading panel is displayed:

Review note
This resource needs to be re-added.

When the server returned:

Review note
This resource needs to be re-added.

Read more from

Can’t find what you were looking for? Drop us a line.

Joel Bjornson
Found a typo?

This blog post is hosted on GitHub here. Feel free to file a ticket or send a PR.

Newsletter

We will not spam you or give your details to anyone.