Warning: Reason support is experimental. We are looking for beta-tester and contributors.

How to bind a JS library for OCaml

Accessing a JS variable, ex: document:

Write in .ml:

let v = (Js.Unsafe.js_expr "window")##.document

Alternatively, the global object can be used. In the browser, it refers to window.

let v = Js.Unsafe.global##.document

and in .mli:

val v : ... Js.t

Be careful the function Js_of_ocaml.Js.Unsafe.js_expr and the value Js_of_ocaml.Js.Unsafe.global are not typed. Verify the library documentation before writing the type.

Binding a JS function

Example from the Js module:

let decodeURI (s : js_string t) : js_string t =
  Js.Unsafe.fun_call (Js.Unsafe.js_expr "decodeURI") [|Js.Unsafe.inject s|]

Have a look at the Js_of_ocaml.Js.Unsafe module API.

Using a JS constructor, ex: F:

Write in .ml:

let f = Js.Unsafe.global##._F

and in .mli:

val f : (... -> ... Js.t) Js.constr

and if you want to use JS overloading, do, for example:

val f_fromInt : (int -> ... Js.t) Js.constr
val f_fromString : (js_string t -> ... Js.t) Js.constr
val f_blah : (#Dom_html.element t -> js_string t -> ... Js.t) Js.constr

Accessing or modifying a JS property to an element

When a property is missing in the OCaml interface of an element (for example it has been dynamically added by a library), you can access using unsafe features:

(Js.Unsafe.coerce elt)##.blah

If you want to add yourself a new property:

(Js.Unsafe.coerce elt)##.blah := v

Here, v may be a JS value or an OCaml value.

If you want to do that in type safe manner, just define new types for the extended elements, or wrap the unsafe functions inside a getter and setter.

Binding a JS object

Write in .ml and in .mli:

class type my_js_type = object

  (* read only property, read value with t##.prop1 *)
  method prop1 : int readonly_prop

  (* write only property, write value with t##.prop2 := 3.14 *)
  method prop2 : float writeonly_prop

  (* both read and write *)
  method prop3 : int prop

  (* method or property starting with a capital letter can be prepend
     with an underscore. *)
  method _Array : ... (* to access the JavasScript property or method Array *)

  (* Define two methods with different types, that translate to
     the same JavaScript method. *)
  method my_fun_int : int -> unit meth
  method my_fun_string : js_string t -> unit meth
  (* Both will actually call the my_fun JavaScript method. *)

  (* To call a javascript method starting with one underscore *)
  method __hiddenfun : ..
  method __hiddenfun_ : ..
  method __hiddenfun_something : ..
  (* This will call the _hiddenfun Javascript method *)

  (* To call the javascript method '_' *)
  method __ : ..
end

Example binding some constants:

For example if the JS class is used to define three constants thelib.Theclass.VALUEA, thelib.Theclass.VALUEB, thelib.Theclass.VALUEC,

Since ocaml doesn't allows method name to start with capitalised letter, we can add an _

write in .ml and .mli:

type thetype

class type theclass = object
  method _VALUEA : thetype readonly_prop
  method _VALUEB : thetype readonly_prop
  method _VALUEC : thetype readonly_prop
end

and in .ml:

let theclass = (Js.Unsafe.js_expr "thelib")##._Theclass

and in .mli:

val theclass : theclass t

Constructing JS objects manually

If you want to construct a JS object manually (without calling a function or a constructor), you can use the Ppx syntax extension.

For example:

let options = object%js
  val x = 3 (* read-only prop *)
  val mutable y = 4 (* read/write prop *)
end

You can also use the unsafe Js_of_ocaml.Js.Unsafe.obj.

Set/get variables

You can access every variable through the global javascript object (window):

If the variable var has type t Js.t

let set (x:t Js.t) = Js.Unsafe.global##.var := x
let get x : t Js.t = Js.Unsafe.global##.var

Object property with multiple types

If you want to read a property of an object which can have multiple types, you can define an intermediate type to do typesafe casting ex:

Suppose the object obj has a property prop which can be either a string or a Dom node:

type dom_or_string

class type obj = object
  method prop : dom_or_string Js.t prop
end

let obj : obj Js.t = Js.Unsafe.js_expr "obj"

let string_constr : Js.js_string Js.t Js.constr = Js.Unsafe.global##._String

let cast_string (x:dom_or_string Js.t) : Js.js_string Js.t Js.opt =
  if Js.instanceof x string_constr
  then Js.some (Js.Unsafe.coerce x)
  else Js.null

let node_constr : Dom.node Js.t Js.constr = Js.Unsafe.global##._Node

let cast_node (x:dom_or_string Js.t) : Dom.node Js.t Js.opt =
  if Js.instanceof x node_constr
  then Js.some (Js.Unsafe.coerce x)
  else Js.null

Check availability of method

It is frequent that some method are not to be implemented in some browser.

To check the presence of method met:

let check_met obj = Js.Optdef.test ((Js.Unsafe.coerce obj)##.met)