Functional Reactive Programming and HTML
WebSharper.UI is a library providing a novel, practical, and user-friendly approach to UI reactivity. It includes:
- An HTML library usable on both server and client sides, allowing you to build HTML pages either by calling F# functions to create elements or by using template HTML files.
- A reactive layer for expressing user inputs and values computed from them as time-varying values, related to Functional Reactive Programming (FRP). This reactive system integrates with the HTML library to create reactive documents. If familiar with React, you'll notice similarities: instead of explicitly inserting, modifying, or removing DOM nodes, you return a value representing a DOM tree based on inputs. The main difference is that these inputs are nodes of the reactive system, not a single state value associated with the component.
- Client-side routing using the same endpoint type declaration as WebSharper server-side routing.
This page is an overview of the capabilities of WebSharper.UI.
Get the package from NuGet: WebSharper.UI.
Using HTML
WebSharper.UI's core type for HTML construction is Doc. A Doc can represent a single DOM node (element, text), but it can also be a sequence of zero or more nodes. This allows you to treat equally any HTML snippet that you want to insert into a document, whether it consists of a single element or not.
Additionally, client-side Docs can be reactive. A same Doc can consist of different elements at different moments in time, depending on user input or other variables. See the reactive page to learn more about this.
Constructing HTML
The main means of creating Docs is by using the functions in the WebSharper.UI.Html module.
Every HTML element has a dedicated function, such as div or p, which takes a sequence of attributes and child nodes, of type Attr and Doc respectively.
Additionally, the text function creates a text node.
Result:
Some HTML tags, such as option, collide with standard library names and are therefore only located in the Tags submodule.
Result:
One thing to note is that the tag functions described above actually return a value of type Elt, which is a subtype of Doc that is guaranteed to always consist of exactly one element and provides additional APIs such as Dom to get the underlying Dom.Element. This subtyping means that you will sometimes need to upcast the result of such a function with :> Doc to appease the compiler; you can see an example of this below in the example for Doc.Empty.
Additional functions in the Doc can create or combine Docs:
-
Doc.Emptycreates a Doc consisting of zero nodes. This can be useful for example when you may not need to insert an element depending on a condition.Result:
-
Doc.Appendcreates a Doc consisting of the concatenation of two Docs.Result:
For the mathematically enclined, the functions Doc.Empty and Doc.Append make Docs a monoid.
-
Doc.ConcatgeneralizesAppendby concatenating a sequence of Docs.Result:
-
Doc.Elementcreates an element with the given name, attributes and children. It is equivalent to the function with the same name from theHtmlmodule. This function is useful if the tag name is only known at runtime, or if you want to create a non-standard element that isn't available inHtml. The following example creates a header tag of a given level (h1,h2, etc).Result:
-
Doc.Verbatimcreates a Doc from plain HTML text.
Security warning: this function does not perform any checks on the contents, and can be a code injection vulnerability if used improperly. We recommend avoiding it unless absolutely necessary, and properly sanitizing user inputs if you do use it. If you simply want to use HTML syntax instead of F# functions, take a look at templating.Result:
Attrs
To create attributes, use corresponding functions from the attr submodule.
Result:
Some attributes, notably class and type, are also F# keywords, so they need to be wrapped in double backquotes.
Result:
HTML5 also defines any attribute whose names starts with data- as a valid custom attribute. You can create such an attribute using the function data- from module attr (backquoted since it contains a non-standard character).
Result:
Like Doc, a value of type Attr can represent zero, one or more attributes. The functions in the Attr module can create such non-singleton attributes.
-
Attr.Emptycreates an empty attribute. This can be useful for example when you may not need to insert an attribute depending on a condition.Result:
-
Attr.Appendcombines two attributes.Result:
-
Attr.Concatcombines a sequence of attributes.Result:
-
Attr.Createcreates a single attribute. It is equivalent to the function with the same name from theattrmodule. This function is useful if the attribute name is only known at runtime, or if you want to create a non-standard attribute that isn't available inattr.Result:
Event handlers
A special kind of attribute is event handlers. They can be created using functions from the on submodule.
The handler function takes two arguments:
- The element itself, as a native
Dom.Element; - The triggered event, as a native
Dom.Event.
In addition to the standard HTML events, on.afterRender is a special handler that is called by WebSharper after inserting the element into the DOM.
HTML on the client
To insert a Doc into the document on the client side, use the Doc.Run* family of functions from the module WebSharper.UI.Client. Each of these functions has two variants: one directly taking a DOM Element or Node, and the other suffixed with ById taking the id of an element as a string.
-
Doc.RunandDoc.RunByIdinsert a given Doc as the child(ren) of a given DOM element. Note that it replaces the existing children, if any. -
Doc.RunAppendandDoc.RunAppendByIdinsert a given Doc as the last child(ren) of a given DOM element. -
Doc.RunPrependandDoc.RunPrependByIdinsert a given Doc as the first child(ren) of a given DOM element. -
Doc.RunAfterandDoc.RunAfterByIdinsert a given Doc as the next sibling(s) of a given DOM node. -
Doc.RunBeforeandDoc.RunBeforeByIdinsert a given Doc as the previous sibling(s) of a given DOM node. -
Doc.RunReplaceandDoc.RunReplaceByIdinsert a given Doc replacing a given DOM node.
HTML on the server
On the server side, using sitelets, you can create HTML pages from Docs by passing them to the Body or Head arguments of Content.Page.
By opening WebSharper.UI.Server, you can also just pass a full page to Content.Page. This is particularly useful together with templates.
This overload that takes a single Doc can also take Status, ExtraContentHeaders, and Bundle arguments (all optional) to set status code, response headers and sitelet bundle name to use.
To include client-side elements inside a page, use the client method, from inside WebSharper.UI.Html.
The overload that accepts Title, Head, Body, Doctype, and Bundle (all optional arguments) is creating a full page automatically, where the head will include any necessary WebSharper scripts to initialize the contents in Body automatically.