Doc.Checkbox 'change' event does not occur in Websharper.UI.Next

165 views Asked by At

I have reactive Var variable varDone and Doc.Checkbox for its representation named cbDoc. After changing the value of varDone I need to call my function. For this, I wrote the code illustrated in the following pseudo-code:

open WebSharper
open WebSharper.UI.Next
open WebSharper.UI.Next.Html
open WebSharper.UI.Next.Notation

// declaring reactive bool variable
let varDone = Var.Create false

(* something else *)

// creting Doc-representation of my model
let renderModel model = 

    // declaring its representation Doc element
    let cbDone = Div0 [ Doc.CheckBox [ "type" ==> "checkbox" ] varDone ]

    let result'doc = // a Doc-representation, that contains cbDone

    let my'handler()  : unit -> unit = // a function that should be called when changing the value of varDone

    // add an event handler for the element cbDone changes
    (Doc.AsPagelet cbDone).Body.AddEventListener( "change", my'handler, false )

    result'doc

But unfortunately no event occurs when checkbox changing. The question is, what am I doing wrong and is there another way to respond to changes in value of varDone variable?

UPD

The saddest thing is that an element with checkbox does not even have my event handler. This is evident when debug in browser.

1

There are 1 answers

1
Tarmil On BEST ANSWER

Edit: This is how the second solution below would be implemented with current WebSharper.UI:

let cbDone =
    div [] [
        Doc.CheckBox [
            attr.``type`` "checkbox"
            on.viewUpdate varDone.View (fun _ _ -> my'handler())
        ] varDone
    ]

What happens is that Doc.AsPagelet adds a wrapper around the doc, so you are adding an event listener to that wrapper. And even if it did not, you would be adding an event listener to the Div0 you created instead of the CheckBox itself.

That being said, there are two solutions here:

  • The easy solution is to use Attr.Handler in your CheckBox:

    let cbDone =
      Div0 [
        Doc.CheckBox [
          "type" ==> "checkbox"
          Attr.Handler "change" (fun _ -> my'handler())
        ] varDone
      ]
    
  • The more complex but generally preferable solution is to listen to changes to varDone via a View, rather than listen to events on the element.

    Now, the way UI.Next works, only views that connect into the sink (ie. the rendered document) are actually computed, so nothing will happen if you only do something like this:

    let _ = varDone.View |> View.Map (fun _ -> my'handler())
    

    Instead, the result value of the above expression (which has type View<unit>) needs to be connected to the returned document. You can use the following helper (which we'll probably add to UI.Next in some form soon):

    let AddViewListener (view: View<'T>) (f: 'T -> unit) (doc: Doc) =
      view
      |> View.Map (fun x -> f x; Doc.Empty)
      |> Doc.EmbedView
      |> Doc.Append doc
    

    which you can use as such:

    let cbDone =
      Div0 [ Doc.CheckBox [ "type" ==> "checkbox" ] varDone ]
      |> AddViewListener varDone.View (fun _ -> my'handler())
    

    The above code basically means "as long as cbDone is in the rendered document, any change to varDone will trigger my'handler".