In the previous Hello WebSharper post i intentionally left a few questions unanswered:

What is the role of the Main.html file in the project?

What solutions does WebSharper offer do interact with HTML content?

We will try to answer those questions in a series of posts dedicated to WebSharper’s capabilities.

General overview of the templating engine

WebSharper comes with a templating engine that helps programmers manipulate their HTML files from F# by using special attributes. Those attributes create holes in the HTML structure that can be filled with data using logic described in F#.

In our Main.fs file, we declared a few modules that we didn’t cover in the previous post. One of them is the Templating module that declares a new record type that represents the structure of a typical page from our website (check out the full code on the GitHub repo):

type Page = { Title : string Body : list<Element> }

In our case, a page is basically made of a Title (some text) and a Body (a list of HTML elements such as div, h1, or p). We then define the MainTemplate function that will map our Page records to an existing HTML file:

let MainTemplate = Content.Template<Page>("~/Main.html") .With("title", fun x -> x.Title) .With("body", fun x -> x.Body)

Content.Template is a WebSharper type that returns an HTML template from a file passed as parameter, in our case Main.html. The two With functions allow to map data defined in F# with holes declared in the HTML markup. For instance, the following line maps the ‘title’ hole to the Title field from our Page record:

.With("title", fun x -> x.Title)

Time to see how those holes are defined in our HTML files. In today’s post, we are going to focus on the simplest type of holes available, text holes.

Warming up with text holes

Text holes, as you could guess, can only contain text. The Title field that we just saw above is mapped to a text hole. This is how the hole itself is defined in Main.html:

<title>${title}</title>

As you can see, a text hole is basically declared as follows: ${name_of_the_hole}

I added to the website a separate Templates page (accessible at localhost:9000/templates) and a new AdvancedTemplating module to help test those bindings. We will extend this module as we go along with this series. Here is the F# code that defines the template and bindings for this page:

module AdvancedTemplating = open System.Web type AdvancedPage = { Title: string SomeTextHole: string } let Template = Content.Template<AdvancedPage>("~/AdvancedTemplate.html") .With("title", fun x -> x.Title) .With("sometexthole", fun x -> x.SomeTextHole) let Main context endpoint title someTextHole : Async<Content<EndPoint>> = Content.WithTemplate Template { Title = title SomeTextHole = someTextHole }

And here is the associated advancedTemplate.html file, very simple for now:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>${title}</title> <style></style> </head> <body> <H1>${sometexthole}</H1> </body> </html>

We can count two text holes there: ‘title’ and ‘sometexthole’. The last piece of the puzzle is the creation of the page itself in the Site module:

let TemplatesPage context = AdvancedTemplating.Main context EndPoint.Templates "Templating in WebSharper" // Our title hole "Just a test page to play with the HTML templating engine in WebSharper!" // Our sometexthole hole

Finally, let’s check how the new page looks:

It worked! ‘Templating in WebSharper’ got mapped to the title HTML tag (the caption of the tab in the browser) while ‘Just a test page to play with the HTML templating engine in WebSharper!’ got mapped to the main H1 element on the page.

As a bonus, we can change the look of our page just by editing our HTML file, without having to recompile the whole F# project. This is useful as your designer can freely fiddle with the UI without bothering the programmers in the team.

That’s it for today. Stay tuned for the next post in the series where we will cover a slightly more complicated mechanism: data holes.

Cheers!