This post shows various ways to handle input using WebSharper.
Client side
There are two basic approaches: using HTML templates or using Websharper’s HTML.
HTML templates give a clean separation between design and function, and you can change the HTML file without recompiling. But there are some limits to it also.
HTML Templating
On the client side, you can combine reactive variables with input html elements.
<label>
Any text:
<input type="text" ws-var="T1" placeholder="enter any text">
</label>
<label>
Any number:
<input type="number" ws-var="N1">
</label>
<label>
Any date:
<input type="date" ws-var="D1">
</label>
<label>
Phone number:
<input type="tel" ws-var="Tel" placeholder="Phonenumber" />
</label>
<label>
Password:
<input type="password" ws-var="PW">
</label>
<label>
OnInput:
<input type="text" ws-oninput="OnInput" />
</label>
<label>
Checkbox:
<input type="checkbox" ws-var="Check" />
</label>
<label>
Selection:
<select ws-var="Color">
<option>green</option>
<option>blue</option>
<option>yellow</option>
</select>
</label>
<p>
Text = ${T1}, Number = ${N1}, Check = ${Check}
</p>
And in the code:
[<JavaScript>]
module Client =
let t1 = Var.Create ""
let n1 = Var.Create 0
let d1 = Var.Create ""
let tel = Var.Create ""
let pw = Var.Create ""
let check = Var.Create true
let color = Var.Create ""
let n2: Var<Client.CheckedInput<int>> = Var.Create (Client.CheckedInput<int>.Blank "0")
let Main () =
MainTemplate()
.T1(t1)
.N1(n1)
.D1(d1)
.Tel(tel)
.PW(pw)
.Check(check)
.Color(color)
.N2(n2)
.OnInput(fun ev -> printfn "ev = %A" ev.Target)
.Doc()
Handling of select
The HTML code is straight forward:
<label>
Primary Colors:
<select ws-var="PrimaryColor" ws-hole="PrimaryColorList">
</select>
</label>
However, the following fsharp will fail:
[<JavaScript>]
...
type Color = Red | Yellow | Blue
let primary = Var.Create PrimaryColors.Blue
let primaryColors =
FSharp.Reflection.FSharpType.GetUnionCases typeof<PrimaryColors>
|> Array.map (fun uc -> uc.Name)
|> Array.map (fun col -> Tags.option [] [text col])
Template()
.PrimaryColor(primary)
.PrimaryColorsList(primaryColors)
It fails for two reasons:
- HTML templating can only handle variables of numeric or string types
- FSharpType does not exist for JavaScript. So during compilation, it will claim
WebSharper error FS9001: Type not found in JavaScript compilation: Microsoft.FSharp.Reflection.FSharpType
.
So try this:
let primary = Var.Create (sprintf "%A" PrimaryColors.Blue)
let primaryColors =
[ Red; Yellow; Blue]
|> Seq.map (sprintf "%A")
|> Seq.map (fun col -> Tags.option [] [text col])
Unfortunately, i did not find an easy way to enumerate over the union, therefore the explicit list [ Red; Yellow; Blue]
.
WebSharper HTML
to be done
File input
To use input type file
for uploading a file, there are two possibilities: posting a form and handle the input in the server, or handle it on the client.
Here we discuss handling it on a client. Further information can be found in at Mozilla or on W3Docs.
In JavaScript, the corresponding code looks as shown below. The actual content of the file is given in the fileReader.result
.
<body>
<input onchange="readFile(this)" type="file">
<script>
function readFile(input) {
let file = input.files[0];
let fileReader = new FileReader();
fileReader.readAsText(file);
fileReader.onload = function() {
alert(fileReader.result);
};
fileReader.onerror = function() {
alert(fileReader.error);
};
}
</script>
</body>
In the WebSharper html template, we use an ws-oninput
trigger.
<label>
file Load:
<input type="file" ws-oninput="LoadFile">
</label>
In F#, we the input event contains the fields Target
and Event
. We use target and cast it to HTMLInputElement
. Two event handlers are used: Onload
and Onerror
. The handlers are called from the ReadAsText
function. In this simple example, we just print the content of the text file to the console.
open WebSharper.UI.Html
open WebSharper.JavaScript
TemplateMain()
.LoadFile(fun event ->
let inputElem = ev.Target :?> HTMLInputElement
let files = inputElem.Files
for i in 0 .. files.Length-1 do
let file = files.[i]
printfn "file = %s, %i" file.Name file.Size
let reader = new TextFileReader()
reader.Onload <- (fun _ -> printfn "reader = %s" reader.Result)
reader.Onerror <- (fun _ -> sprintf "cannot read %s" file.Name |> JS.Alert)
reader.ReadAsText(file)
)