Friday, July 20, 2018

checkboxes with Suave.IO, using dotliquid tamplates



Using checkboxes with Suave.Io

The Suave.IO web library for F# adopts two ways of rendering html.

By using Suave.form/Suave.html the page is represented as a native F# list, which basically consists in html elements, or nodes, and forms which are defined by specific records. Invalid html page is impossible in this way because the syntax check is strict.

Suave.dotliquid instead adopts old fashion html text + some variable substitution at server side.

However, Suve.Form/Suave.html misses support for checkboxes yet.

We can't do the following stuff to render a checkbox form, whereas we can do if we used other supported types for html widget (see https://theimowski.gitbooks.io/suave-music-store/content/en/form_module.html)

Defining the form.

type UserEdit = {
    Enabled: bool
    CanVoidOrder: bool
    CanManageAllorders: bool
    CanChangeThePrices: bool
}


let userEdit: Form =
   Form ( [
            Checkbox ((fun f -> <@ f.Enabled @>), [])
            Checkbox ((fun f -> <@ f.CanVoidOrder @>), [])
            Checkbox ((fun f -> <@ f.CanManageAllorders @>), [])
            Checkbox ((fun f -> <@ f.CanChangeThePrices @>), [])
           ],
         [])

REnd the form in the ui:

renderForm
       { Form = Form.userEdit
         Fieldsets =
             [ { Legend = "Modify user"
                 Fields =
                     [
                       { Label = "enabled"
                         Html = Checkbox (fun f -> <@ f.Enabled @>) ["value", true]  )
                       }                   
                     ]
                     [
                       { Label = "can manage all orders"
                         Html = Checkbox (fun f -> <@ f.CanVoidOrder @>) ["value", true]  )
                       }                   
                     ]
                     [
                       { Label = "can void order"
                         Html = Checkbox (fun f -> <@ f.CanManageAllorders @>)["value", true]  )
                       }                   
                     ]
                     [
                       { Label = "can change the prices"
                         Html = Checkbox (fun f -> <@ f.CanChangeThePrices @>)["value", false]  )
                       }                   
                     ]


                }
             ]
         SubmitText = "modify" }

This is probably how it will be possible in the future, but is not possible yet. bool and chekbox are not supported.

So I needed dotliquid templating to overcome, where any html is valid, because it is actual html plain text code.

A recap of checkboxes in html:



<form method="post">
<input name="myName1" nbsp="" type="checkbox" />myValue1</input>
<input name="myName2" nbsp="" type="checkbox" />myValue2</input>
<input type="submit" update="" />



In posting this form, it evaluates myName1 to myValue1 if checked, and  myValue2 to myValue2  if checked.

So what I we can do to use dotliquid forms for checkboxes is using option types. I don't really care about the value passed, but only if they are passed or not. For that reason option types fit the purpose.

type UserEdit = {
    Enabled: string option
    CanVoidOrder: string option
    CanManageAllorders: string option
    CanChangeThePrices: string option
}

//wa can leave the following empty (no validators an so on)..
let userEdit: Form =
   Form ( [
           ],
         [])

So the code in App.fs would be like:

type modelForLiquid = {name: string} // how to pass parameters to the template, if needed

let manageUser (Db.user) =
choose [
     GET  >=>
          let o = {name = "yourname"}
          DotLiquid.page("template.html") o
 
     POST >=>
          bindToForm Form.userEdit (fun form ->
              let ctx  = db.getContext()
              let isEnabled = form.Enabled.isSome
              let canVoidOrder = form.CanVoidOrder.isSome
              ......
              Db.UpdateUser user.UserId isEnabled canVoidOrder ... ctx

     ]

Something similar should be needed for radiobuttons.

What we need to be sure about is that we use POST rather than GET in the form, and how html pass the parameters. Basically they are a list of name value pairs.
The POST part of our code in the controller (App.fs usually) can handle them in a straightforward way as seen in the code above.

Can be good to know, just in case ;-)




No comments: