File: intro.wiki

package info (click to toggle)
tyxml 4.6.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 944 kB
  • sloc: ml: 9,712; makefile: 91; javascript: 3
file content (153 lines) | stat: -rw-r--r-- 7,574 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
= TyXML =

Tyxml is a library for building statically correct HTML and SVG documents.

<<code language="ocaml"|
open Tyxml
let to_ocaml = Html.(a ~a:[a_href "ocaml.org"] [txt "OCaml!"])
>>

==@@id="usage"@@ Using TyXML ==

=== Standalone Use ===

To use TyXML in standalone manner, simply install the ##tyxml## OPAM package, link the ##tyxml## ocamlfind library and open <<a_api|module Tyxml>>.

=== Use with another library ===

TyXML combinators can be used in conjunction with other libraries. Please consult the relevant document. For example,
<<a_manual project="eliom" chapter="clientserver-html"|Eliom>>
and
<<a_api project="js_of_ocaml" subproject="js_of_ocaml-tyxml" text="Js_of_ocaml"|module Js_of_ocaml_tyxml.Tyxml_js>>.

=== Use with the PPX ===

TyXML can also be used with the standard HTML syntax, using <<a_manual chapter="ppx"|the PPX syntax extension>>:
<<code language="ocaml"|
open Tyxml
let%html to_ocaml = "<a href='ocaml.org'>OCaml!</a>"
>>

This syntax is available both while using TyXML standalone, or with another library. In order to do so, install the ##tyxml-ppx## OPAM package and link the ##tyxml.ppx## ##ocamlfind## library.

==@@id="example"@@ Examples and documentation

For standalone use, examples are available in the [[https://github.com/ocsigen/tyxml/tree/master/examples|examples]] directory. The entry point of the documentation is in the <<a_api|module Tyxml>> module.

==@@id="creation"@@ Creating documents with TyXML ==

This section assumes you have at your disposal an {{{Html}}} or {{{Svg}}} module, as instructed in <<a_manual fragment="usage"|the previous section>>.

The documentation for TyXML combinators is provided in <<a_api|module Html_sigs.T>> and <<a_api|module Svg_sigs.T>> and is common to all instances of {{{Html}}} and {{{Svg}}}.

The first thing to understand about TyXML is that for most intents and purposes, it is exactly like HTML. As such, the [[https://developer.mozilla.org/en-US/docs/Web/HTML/Element|HTML reference]] is still very useful. For each HTML element or attribute, there is a combinator implementing it. The main differences are that you can use OCaml to manipulate the elements and that invalid markup produces a type error.

In this tutorial, we will build the [[https://github.com/ocsigen/tyxml/tree/master/examples/mini_website|Mini website]].
If you prefer the native HTML syntax, you can also use the <<a_manual chapter="ppx"|PPX syntax extension>> and consult the [[https://github.com/ocsigen/tyxml/tree/master/examples/mini_website_ppx|PPX mini website]].

Let us start by building the content of our website. For text, we use the {{{txt}}} combinator. In traditional Web fashion, we put everything in a ##div##.
<<code language="ocaml"|
let mycontent =
  div [
    txt "This is a fabulous content." ;
  ]
>>

The variable ##mycontent## is of type {{{[> `Div] Html.elt}}}.
As we can see, the fact that this is a ##div## is reflected in the type. HTML elements are of type <<a_api text="elt"|type Html_sigs.T.elt>> and have a combinator of the same name, except when it's a reserved OCaml keyword (such as {{{object_}}}).

Our content is fabulous, but for the sake of CSS styling (and still in true Web fashion) we want to add a ##class## to it.
<<code language="ocaml"|
let mycontent =
  div ~a:[a_class ["content"]] [
    txt "This is a fabulous content." ;
  ]
>>

The <<a_api text="a_class"|val Html_sigs.T.a_class>> creates a new ##class## attribute of type {{{[> `Class] attrib}}}. Similarly to elements, the kind of attribute is reflected in the <<a_api text="attrib"|type Html_sigs.T.attrib>> type.
We use the optional argument {{{~a}}} to pass the list of attributes. This optional argument is available on all element combinators.

In order to add a title to our fabulous content, we use the <<a_api text="h1"|val Html_sigs.T.a>> combinator.
<<code language="ocaml"|
let mycontent =
  div ~a:[a_class ["content"]] [
    h1 [txt "A fabulous title"] ;
    txt "This is a fabulous content." ;
  ]
>>

Naturally, {{{div}}} accepts several children. In TyXML vocabulary, this is a <<a_api text="star"|type Html_sigs.T.star>> combinator.
There are also
<<a_api text="unary"|type Html_sigs.T.unary>> and
<<a_api text="nullary"|type Html_sigs.T.nullary>> combinators,
which accept, respectively, one child and zero children.

<<a_api text="title"|val Html_sigs.T.title>> is an example of a {{{unary}}}
combinator.
<<code language="ocaml"|
let mytitle = title (txt "A Fabulous Web Page")
>>

====@@id="type-errors"@@ Interlude about type errors ====

However, what would happen if we were to try to put **bold** text in our title? This is not specification-compliant! Let's try it.
<<code language="ocaml"|
let mytitle = title (b [txt "A Bold Web Page"])
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: This expression has type ([> Html_types.b ] as 'a) elt
       but an expression was expected of type
         ([< Html_types.title_content_fun ] as 'b) elt = 'b elt
       Type 'a = [> `B ] is not compatible with type 'b = [< `PCDATA ]
       The second variant type does not allow tag(s) `B
>>

As expected, this code does not type-check!
The type checker is unfortunately a bit unclear about the source of the error.

It tells us that the given expression has type {{{[> b] elt}}}
(indeed, it is produced by a {{{b}}} combinator)
but an expression is expected of type {{{[< title_content_fun] elt}}}
(which means that is is used as content for a {{{title}}} element).
It then tells us that, since {{{[> b] = [> `B]}}} and
{{{[< title_content_fun] = [< `PCDATA ]}}},
{{{`B}}} is not allowed inside a {{{title}}}.

In order to get reasonable type errors with TyXML, The ##-short-paths## option should always be used when invoking OCaml.

====@@id="finishing"@@ Finishing up the webpage ====

To finish our webpage, we use <<a_api text="body"|val Html_sigs.T.body>>, <<a_api text="head"|val Html_sigs.T.head>> and <<a_api text="html"|val Html_sigs.T.html>>.
The last two combinators have special types due to their specific constraints: <<a_api text="head"|val Html_sigs.T.head>> requires only one ##title## child, and <<a_api text="html"|val Html_sigs.T.html>> requires exactly two children: ##head## and ##body##.

<<code language="ocaml"|
let mypage =
  html
    (head mytitle [])
    (body [mycontent])
>>

If you are using <<a_manual project="eliom" chapter="clientserver-html"|Eliom>>
or <<a_api project="js_of_ocaml" subproject="js_of_ocaml-tyxml" text="Js_of_ocaml"|module Js_of_ocaml_tyxml.Tyxml_js>>,
this is the end of TyXML's territory. However, for standalone use, we now need to print our document as an HTML file. The standalone implementation comes with a printer, <<a_api|val Tyxml.Html.pp>>, that we can use to print files:

<<code language="ocaml"|
let () =
  let file = open_out "index.html" in
  let fmt = Format.formatter_of_out_channel file in
  pp () fmt mypage;
  close_out file
>>

You could also print directly to a string:
<<code language="ocaml"|
let s = Format.asprintf "%a" (Html.pp ()) mypage
>>

Well done, you know have a very minimal (but fabulous) website! Once again, the implementation can be found [[https://github.com/ocsigen/tyxml/tree/master/examples/mini_website|here]].

Other examples are available in the [[https://github.com/ocsigen/tyxml/tree/master/examples/|examples]] directory.

==@@id="custom"@@ Using your own underlying implementation ==

You can use TyXML with any underlying implementation. In order to do so, TyXML provides a set of functors.
Please consult <<a_manual chapter="functors"|the relevant manual>>.