Browser interfaces are
described by a tree of HTML
elements, each of which
can have some
attributes attached.
The virtual_dom
library provides an OCaml
interface for constructing these
trees.
This wouldn’t be a
programming tutorial without a
hello world example, which
introduces the
Vdom.Node.text
node
constructor.
let hello_world : Vdom.Node.t = Vdom.Node.text "hello world!"
The text node will frequently be the “leaf” of a view (there are no “children” of a text node). Let’s put some text inside a bulleted list by using some more node constructors:
let bulleted_list : Vdom.Node.t =
let open Vdom.Node in
div"Norwegian Pancakes" ]
[ h3 [ text
; ul"3 eggs" ]
[ li [ text "2 cups of milk" ]
; li [ text "1 cup of flour" ]
; li [ text
]
] ;;
For the bulleted list, the
ul
and
li
functions are
required. These correspond to
the ul
element and the li
element, which MDN helpfully
tells us stands for
Unordered List and
List Item.
h3
is short for
“header level 3”, and is
responsible for the larger font
in the title text, and
div
is a “content
division” and serves as a
useful wrapper for the rest of
the content.
An optional argument to the
Vdom.Node.*
constructor functions is a
Vdom.Attr.t list
.
These Attr.t
correspond to DOM
attributes, DOM
properties, and DOM
event_handlers.
Attributes can be used to tweak the appearance and behavior of the nodes that they are attached to, for instance, by adding placeholder text to a textbox:
let input_placeholder : Vdom.Node.t =
input ~attr:(Vdom.Attr.placeholder "placeholder text here") ()
Vdom.Node. ;;
Or coloring text with inline css:
let css_gen : Vdom.Node.t =
Vdom.Node.span"red")))
~attr:(Vdom.Attr.style (Css_gen.color (`Name "this text is red" ]
[ Vdom.Node.text ;;
The 8th chapter “css” goes into much more depth on the styling attributes.
Finally, there are “event handler” attributes which register functions that are called when a user interacts with the element (like clicking on buttons or typing into a text box).
let clicky : Vdom.Node.t =
Vdom.Node.button
~attr:fun (_evt : mouse_event) ->
(Vdom.Attr.on_click ("hello there!";
alert
Ui_effect.Ignore))"click me!" ]
[ Vdom.Node.text ;;
These functions usually
receive a browser-level event
value (ignored in the above
example as _evt
)
alongside any useful data
extracted from that event. For
example, see the following
event-handler attributes for
mouse-clicks and typing into a
textbox:
val Vdom.Attr.on_click : (mouse_event -> unit Vdom.Effect.t) -> Vdom.Attr.t val Vdom.Attr.on_input : (input_event -> string -> unit Vdom.Effect.t) -> Vdom.Attr.t
You’ll notice that because
on_input
is used to
respond to users typing into a
textbox, the handler function is
also given a string that holds
the current contents of that
textbox.
The browser-level
event-values like
mouse_event
and
input_event
are
almost always ignored in Bonsai
apps.
The return type for these
event handler functions is
unit Vdom.Effect.t
,
which is the final type that we
care about in the
Virtual_dom
library.
In the example above, the
on_click
handler
function returned
Vdom.Effect.Ignore
.
However, the alert definitely
fires when you click on it, so
what is this value doing, and
why must these event-handlers
return values of type
unit Vdom.Effect.t
in the first place?
In reality, values of type
unit Vdom.Effect.t
are used to schedule work on
Bonsai’s event-queue.
Vdom.Effect.Ignore
is the no-op event, and it
schedules no work on the
event-queue.
Vdom.Effect.Many [a; b; c]
wraps up multiple events,
scheduling them all in
order.
That leaves us with two more question:
unit Vdom.Effect.t
that aren’t just
Ignore
and
Many
Both of which will be answered in Bonsai Guide Part 3: State.
A virtual-DOM is an immutable tree of immutable UI elements that represents the view of the application at a point in time. This is in contrast to the DOM (Document Object Model), which is a mutable tree of mutable UI elements.
With the (not-virtual) DOM, the program mutates the tree of UI components in order to update the view, but with the virtual-DOM, the program produces a new tree every time the view changes. While this may appear to be a performance nightmare, many of the tools that we use to reduce duplication of work in regular programs also work well to prevent re-computing parts of this sub-view.
The Virtual_dom
library also contains functions
that diff two versions of a
virtual-dom tree. The diff can
be used as instructions for
mutating the DOM to reflect the
contents of the “next”
virtual-DOM node. These
functions are quite fundamental,
but Bonsai handles the calls to
these functions, so application
developers are solely concerned
with producing new vdom
trees.
Let’s continue to Bonsai Guide Part 2: Dynamism.