We hope to offer a small but comprehensible tutorial here. At the end of this tutorial, you should be able to write all sorts of HTML templates using BlazeHtml.
Please note that you should at least know some basic Haskell before starting this tutorial. Real World Haskell and Learn You a Haskell are two good starting points.
Installation
The installation of BlazeHtml should be painless using cabal install
:
[jasper@alice ~]$ cabal install blaze-html
Overloaded strings
The OverloadedStrings
is not necessarily needed to work with BlazeHtml, but it is highly recommended, as it allows you to insert string literals in your HTML templates without having boilerplate function calls.
> {-# LANGUAGE OverloadedStrings #-}
Modules
This tutorial is a literate haskell file, thus we should begin by importing the modules we are going to use. To avoid name clashes, we just import everything while renaming the module namespace so that we can use unqualified names most of the time and revert to nice and short qualified names when there is a clash.
> import Control.Monad (forM_)
> import Text.Blaze.Html5 as H
> import Text.Blaze.Html5.Attributes as A
As you can see, we imported the Html5
modules. Alternatively, you can choose to import Text.Blaze.Html4.Strict
. More HTML versions are likely to be added in the future.
A first simple page
The main representation type in BlazeHtml is Html
. Therefore, your “templates” will usually have the type signature ArgumentType1 → ArgumentType2 → Html
. We will now write a small page that just contains a list of natural numbers up to a given n
.
> numbers :: Int -> Html
Note how these templates are pure. It is therefore not recommended to mix them with IO
code, or complicated control paths, generally – you should separate your “view” code from your “logic” code – but you already knew that, right?
> numbers n = docTypeHtml $ do
> H.head $ do
> H.title "Natural numbers"
> body $ do
> p "A list of natural numbers:"
> ul $ forM_ [1 .. n] (li . toHtml)
We use the docTypeHtml
combinator which is basically the doctype followed by the <html>
tag.
Attributes
We also provide combinators to set attributes on elements. Attribute setting is done using the !
operator.
> simpleImage :: Html
> simpleImage = img ! src "foo.png"
Oh, wait! Shouldn’t images have an alternate text attribute as well, according to the recommendations?
> image :: Html
> image = img ! src "foo.png" ! alt "A foo image."
As you can see, you can chain multiple arguments using the !
operator. Setting an attribute on an element with context also uses the !
operator:
> parentAttributes :: Html
> parentAttributes = p ! class_ "styled" $ em "Context here."
As expected, the attribute will only be added to the <p>
tag, and not to the <em>
tag. This is an alternative definition, equivalent to parentAttributes
, but arguably less readable:
> altParentAttributes :: Html
> altParentAttributes = (p $ em "Context here.") ! class_ "styled"
Nesting & composing
It is very common to nest, compose and combine multiple templates, snippets or partials – use whatever terminology you prefer here. Again, a small example. Say we have a simple datastructure:
> data User = User
> { getUserName :: String
> , getPoints :: Int
> }
If the user is logged in, we want to have a snippet that displays the user’s current status.
> userInfo :: Maybe User -> Html
> userInfo u = H.div ! A.id "user-info" $ case u of
> Nothing ->
> a ! href "/login" $ "Please login."
> Just user -> do
> "Logged in as "
> toHtml $ getUserName user
> ". Your points: "
> toHtml $ getPoints user
Once we have this, we can easily embed it somewhere else.
> somePage :: Maybe User -> Html
> somePage u = html $ do
> H.head $ do
> H.title "Some page."
> body $ do
> userInfo u
> "The rest of the page."
In the previous example, the user would probably be pulled out of a reader monad instead of given as an argument in a realistic application.
Getting the goods
Now that we have constructed a value of of the type Html
, we need to do something with it, right? You can extract your data using the renderHtml
function which has the type signature Html → L.ByteString
. This function can be found in the Text.Blaze.Renderer.Utf8
module.
A lazy ByteString
is basically a list of byte chunks. The list of byte chunks the renderHtml
is your HTML page, encoded in UTF-8. Furthermore, all chunks will be nicely-sized, so the overhead is minimal.
There are other renderers as well – for example there is a prettifying renderer called Text.Blaze.Renderer.Pretty
, and if you just want a String
, use Text.Blaze.Renderer.String
.
The blaze-from-html tool
There is also a tool called blaze-from-html
which is used to convert HTML pages to Haskell code using the BlazeHtml library. It needs to be installed seperately using cabal:
[jasper@alice ~]$ cabal install blaze-from-html
The reason that we use a seperate package is because we want to keep the set of blaze-html
dependencies small. blaze-from-html
usage is pretty straightforward. An example:
[jasper@alice ~]$ curl -S http://jaspervdj.be/blaze | blaze-from-html
will output the Haskell code that would be needed to produce this page. By default, blaze-from-html
will use HTML5. You can use other variants as well:
[jasper@alice ~]$ blaze-from-html -v html4-transitional index.html
To include the imports as well, use the -s
flag – this will give you a piece of standalone code that can be compiled directly. The -e
flag causes blaze-from-html
to ignore a lot of errors, which might come in handy if the page you are trying to convert has some faults in it.
Further examples
This tutorial should have given you a good idea of how BlazeHtml works. We have also provided some more real-world examples, you can find them all in this directory on github.
Go forth, and generate some HTML!