Here's a solution that would support an empty element by default:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
type Doc with
    static member SelectDefault attrs show (options : 'T list) (def : Var<'T option>) =
        let setIndex (i : int) (el : Dom.Element) =
            el?selectedIndex <- i

        let el =
            selectAttr [
                yield on.change (fun el _ev -> 
                    let idx : int = el?selectedIndex
                    if idx >= 0 then
                        Var.Set def <| Some options.[idx])
                yield! attrs
            ] (options
                |> List.mapi (fun i o ->
                    Doc.Element "option" [attr.value <| string i] [text <| show o] :> _))
                
        setIndex -1 el.Dom
        Doc.Concat [
            el
            def.View
            |> View.Map (fun v ->
                v |> Option.iter (fun e -> 
                    let idx = List.findIndex ((=) e) options
                    setIndex idx el.Dom)
                Doc.Empty)
            |> Doc.EmbedView
        ]

let Main() =
    let rv = Var.Create None
    let data = ["Option A"; "Option B"; "Option C"]
    let renderOption() = 
        Doc.SelectDefault [
            attr.``class`` "form-control"
        ] (fun f -> f) data rv

    div [
        div [renderOption()]
        rv.View |> View.Map (fun e -> defaultArg e "None") |> textView
    ]
By on 9/8/2015 1:33 AM ()

Yes this is what i need, thanks a lot.

By on 9/8/2015 12:03 PM ()

The temporary solution is use selectAttr insetad of Doc.Select but i loose the reactive var,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let Main() =
    let data = ["Option A"; "Option B"; "Option C"]
    let renderOption data = 
        selectAttr [attr.``class`` "form-control"] (
            data
            |> List.map (fun item ->
                Tags.option [text item] :> _)
        )
    let input = inputAttr [attr.value ""] []
    let output = h1 []
    div [
        input
        buttonAttr [
            on.click (fun _ _ ->
                async {
                    let! data = Server.DoSomething input.Value
                    output.Text <- data
                }
                |> Async.Start
            )
        ] [text "Send"]
        hr []
        h4Attr [attr.``class`` "text-muted"] [text "The server responded:"]
        divAttr [attr.``class`` "jumbotron"] [output]
        renderOption data
    ]
By on 9/6/2015 5:17 AM ()

I found the issue, it's the reactive var cannot be declared as empty string, i don't understand why because in another dom nodes like checkboxes as radio buttons i can use a empty string without this exception.

By on 9/7/2015 1:21 AM ()
1
2
let rv = Var.Create "Option A"
let data = ["Option A"; "Option B"; "Option C"]

rv's initial value has to be in the list as this will be marked as selected by default.

By on 9/7/2015 3:36 AM ()

Cool thanks, makes sense. Another cuestion if i want to update another selection box using for example "Option A" why should use handler on.change or View<'T>

By on 9/7/2015 1:32 PM ()

Sorry I don't quite understand your question. Could you give an example code (or pseudo-code) about what you would like to achive?

By on 9/8/2015 12:14 AM ()

Sure, this is de idea i made with jQuery but i don't now how to do this with UI next:

https://gist.github.com/morellana88/d0877485c37271d75190

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
let firstSelect (data: string list) =
    selectAttr[
        attr.id "first-combo"
        attr.``class`` "form-control"
        on.change(fun _ e ->
            e.PreventDefault()
            let selectedOption = JQuery.Of("#first-combo").Val() |> string
            updateSecondSelect selectedOption "#second-combo"
        )
    ](
    data
    |> List.map (fun item -> Tags.option [text item] :> Doc)
    )

let secondSelect (data: string list) =
    selectAttr[attr.``class`` "form-control"; attr.id "second-combo"]
        (data
            |> List.map (fun item -> Tags.option [text item] :> Doc)
        )

let updateSecondSelect (id: string) = 
    let newOptions = ["Option D"; "Option E"; "Option F"]
    JQuery.Of(id).Empty().Ignore
    newOptions
    |> List.iter (fun opt -> 
        let newOption = JQuery.Of("""<option value=""" + opt + """>""" + opt + """</option>""")
        JQuery.Of(id).Append(newOption).Ignore)
    JQuery.Of("#second-combo").Focus().Ignore
By on 9/8/2015 11:58 AM ()

If you want to update an element based on the value of a reactive var you should use views. I'm still not crystal clear how you want to update the second select but hope this helps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
open WebSharper.UI.Next
open WebSharper.UI.Next.Html
open WebSharper.UI.Next.Client

let firstSelect (data: string list) rvSelected =
    let rv = Var.Create data.[0]
    Doc.Select [attr.``class`` "form-control"] id data rvSelected

let secondSelect (data: string list) (rvSelected : Var<string>) (dependsOn : View<string>) =
    dependsOn
    |> View.Map (fun e ->
        let l = e :: data
        // ensure that the value of the var is in the produced list
        // could use None here if using the select variant with
        // default value
        if not <| List.exists ((=) rvSelected.Value) l then
            Var.Set rvSelected e
        Doc.Select [attr.``class`` "form-control"] id l rvSelected)
    |> Doc.EmbedView

let render () =
    let a = ["A"; "B"; "C"]
    let b = ["1"; "2"]

    let first  = Var.Create a.[0]
    let second = Var.Create b.[0]

    Doc.Concat [
        firstSelect a first
        secondSelect b second first.View
    ]
By on 9/9/2015 1:50 AM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper