Every page in this tutorial has certain things in common -- the header and menu bar for example. You wouldn't want to have have to change the menu bar on every single page if there was a new menu item.
This is why you need a templating system.
HAppS doesn't care much what templating system you use. I use the HStringTemplate package to get the job done, so that's the syntax you'll be seeing in what follows.
A templating system also helps you individualize output. The way this works is by inserting variable text into placeholder templates. For instance, the menu bar in this tutorial displays "logout your_username" if you are logged in, rather than the login/register options. The line below does this too, just for teaching purposes. If you are logged in, it will display your username.
Logged In? Let's see: $ loggedInUser $
Have a look at the template responsible for the content pane of this page. It's pretty boring, except the line above reads as
" Logged In? Let's see: \$ loggedInUser \$ "
Now, if you are running this tutorial locally, load up ghci by running ./hackInGhci
You can see the effect of rendering the current content pane by calling
*Main> :m +Misc View Text.StringTemplate
*Main Misc View Text.StringTemplate> do templates <- directoryGroup "templates" ; writeFile "output.html" \$ renderTemplateGroup templates [] "templates-dont-repeat-yourself"
and then opening the file output.html in firefox. (On ubuntu, in ghci, I just do ":! firefox output.html &" and the file opens in a new tab in firefox.)
To see how this page would look if you were logged in:
*Main Misc View Text.StringTemplate> do templates <- directoryGroup "templates" ; writeFile "output.html" \$ renderTemplateGroup templates [("loggedInUser","DarthVader")] "templates-dont-repeat-yourself"
and reopen output.html in your browser.
As you may have noticed, the html written by the above command is only the current content pane, not the header or table of content links. To render a full page from ghci, as it would appear for a logged in user, try
*Main Misc View Text.StringTemplate> do templates <- directoryGroup "templates"; writeFile "output.html" \$ tutlayout (RenderGlobals templates (Just \$ User "Darth" "" undefined undefined)) [] "templates-dont-repeat-yourself"
Note that tutlayout is a pure function. The only IO is in fetching the templates with directoryGroup. Currently the tutorial does a directory read every time the main webserver handler loops, but it could just as easily only do the directory read once at app startup time. This would mean a lot less disk reads, but of course you would have to stop/start the server whenever a template changed to see your changes. Which setup you want depends on whether you are willing to sacrifice development convenience for a snappier server. I opted for convenience, but it would be easy to change -- just move the directoryGroup command out of the tutorial handler and into Main.hs~ before the server starts. Try it as an exercise if you like.
As a side note, you can get away with passing undefined values to the tutlayout function because it never attempts to evaluate the profile and jobs bits of the User data object. That's lazy evaluation at work.
If you reload output.htlm, you'll see you are missing the header image and css because you're opening a plain html file rather than a page being served by happs (which knows where to find the images and css), but other than that the layout is complete.
You can get a sense for how this all works by looking at the tutlayout function in src/View.hs.
It's not too much fun to develop a web page by outputting a string to a static file and then opening it in a browser every time something changes, so the next thing you might want to try is actually modifying the current template (in ./templates/templates-dont-repeat-yourself.st) with some random text, reloading this actual page, and watching your changes appear. $!
You might have also noticed that the table of contents-style navigation links in the left pane change colors depending on what page is selected. You could have a look at the the /templates/tableofcontents.st to get another taste of how templating works. Here, HAppS looks at each request to determine what the page was called. If the page matches anything in a certain list, the link class gets set to an "active" value, otherwise it gets a default value. !$
We'll learn some StringTemplate basics next.