Interfacing with dynamic JavaScript APIs in WebSharper

Joel Bjornson

Joel Bjornson

Jun 21, 2011

Reading time:

3 mins

Share via:

One of our visions with WebSharper is to enable you to do do all aspects of client side web development in F#. First and foremost by translating F# code into efficient JavaScript code, but also by providing bindings (and the ability to create new bindings) to a variety of popular JavaScript libraries such as jQuery, GoogleMaps and O3D.

Consider for example the function addClass from jQuery. The WebSharper API contains two overloaded versions representing the different ways of invoking the method. For example:

JQuery.Of("#myTable").AddClass("newClass")

JQuery.Of("#myTable").AddClass(fun (ix, _) ->
    if ix % 2 = 0 then
        "evenRow"
    else
        "oddRow"
)

The typedness of the F# API effectivly restricts the user from invoking methods in an invalid way. The following examples result in a compilation error:

JQuery.Of("#myTable").AddClass(33)
JQuery.Of("#myTable").AddClass(fun _ -> ())

However, there are some dynamic features in some JavaScript libraries that are not straight forwardly mapped to a typed setting.

As an example imagine a JavaScript method setCss that only accepts its arguments as an object of name/value pairs for setting CSS properties. In JavaScript you would use the method as in:

node.setCss({
    background-color : "#CCC",
    padding : 10
});

But how would you map this to F#? Unless constructing a configuration type that enlists all CSS properties, you have to fall back to using obj as the type of the argument to the function.

Invoking the function from F# is no longer as straightforward as the corresponding JavaScript code, and typically require you to define additional types covering the different ways in which you are using the function.

The example from above can be written in F# as:

type CssProps =
    {
        background-color : string
        padding : int
    }

node.SetCss
    {
        background-color = "#CCC"
        padding = 10
    }

The additional boilerplate code for defining types can certainly be annoying. For this purpose, it may be convenient to mimic the ability to create anonymous types (as achieved by the record syntax in JavaScript).

Fortunately, this is quite possible using a few utility functions:

[<JavaScript>]
let (:=) x y : (string * obj) = (x, y)

[<Inline "void($r[$k]=$v)">]
let Set (r: obj) (k: string) (v: obj) : unit = ()

[<JavaScript>]
let (!) (fields: seq<(string * obj)>) =
    let r = obj ()
    for (k,v) in fields do
        Set r k v
    r

The (!) operator accepts a sequence of name/value pairs and returns a JavaScript object populated with the corresponding fields.

The same example can now be written as:

SetCss ![
    "background-color" := "#CCC"
    "padding" := 10
]

This is obviously not type-safe, but at least it enables you to interface with dynamic aspects of JavaScript with very little syntactic overhead. A feature for creating anonymous objects (like in the example above) will be included in the next release of WebSharper.

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.