This is a tad misleading. Instead of creating a brand new page, we're going to modify the existing home page and cover a couple of things about Selmer along the way. But first, we need to determine where the HTML is for the home page.
Recall that our hipstr.handler/app
uses three Ring handlers: home-routes
, base-routes
, and test-routes
. We know that test-routes
doesn't handle the home page because we just created it, and we can see that base-routes
is defined in hipstr.handler
and is responsible for handling "HTTP 404: Page Not Found" responses and requests to our static resources. This leaves the home-routes
, which in hindsight, is pretty obvious given the name.
The home-routes
handler is defined in the hipstr.routes.home
namespace (/src/hipstr/routes/home.clj
). The namespace defines two routes and two functions responsible for rendering each respective route's response. Notably, we see the following code right near the bottom:
(GET "/" [] (home-page))
This route serves hipstr's root page, whose content is generated by the home-page
function.
The home-page
function isn't doing much. In fact, all it does is call hipstr.layout/render
, and provide the template name and a context map of values:
(defn home-page [] (layout/render "home.html" {:content (util/md->html "/md/docs.md")}))
The preceding code shines a golden nugget about Luminus: it supports rendering Markdown out of the box. This book will not get into Markdown, however, you can read about its splendid syntax at http://daringfireball.net/projects/markdown/syntax.
The home.html
template in /resources/templates/home.html
is where the visual meat of this route lives. Ultimately, it's just an HTML file with some Selmer markup. Let's discuss a few basic things about Selmer markup before we start tearing away at the template, specifically variables, tags, and filters.
A templating system wouldn't be very useful if it didn't have the notion of variables. If we didn't have variables, then we'd never be able to get dynamic data into our template and then all the puppies in the world would suddenly be very, very sad.
Variables in Selmer are denoted using double curly braces, such as {{variable-name}}
. The value of the variable is determined by a matching key in the context map passed to the template via the layout/render
function. In the home-route
function, explained previously, we passed a context map with a single key:
{:content (util/md->html "/md/docs.md")}
That key is now available as a Selmer variable called content
, and we can render its HTML escaped value to the browser by sticking it between two curlies:
{{content}}
This is exactly what is happening in the /resources/templates/home.html
file:
<div class="row-fluid"> <div class="span8"> {{content|safe}} </div> </div>
Assuming a context map of {:content "Hello World!"}
, the preceding fragment would actually be rendered as this:
<div class="row-fluid"> <div class="span8"> Hello World! </div> </div>
Variables don't have to be flat, however, they can also be structured. For example, pretend we had the following context map:
{:person {:first-name "Ryan" :last-name "Baldwin" :favourite-animal "Elephant"}}
This would resolve to a structured Selmer variable that we could dig into using dot-notation.
<div class="row-fluid"> <div class="span8"> Hello {{person.first-name}} {{person.last-name}}! I see your favourite animal is the {{person.favourite-animal}}. How exciting! </div> </div>
However, what happens if we try and pull the value of a variable that's not defined in the context map? For example, what if we changed the preceding code to read:
I see you prefer your {{person.favourite-animal}} to be {{person.favorite-color}}. How odd.
Since our context map did not define a favorite-color
key, {{person.favorite-color}}
will simply resolve to an empty string.
Filters are basically functions that operate over a variable's value. In our previous discussion about variables, we saw the following code:
{{content|safe}}
Here, we apply the safe
filter to the value of content
or, put another way, we are passing the value of content
into the function that sits behind the safe
filter. The result of the filter is what's subsequently rendered. In our example, safe
restricts HTML escaping the value of content
. So, if the value of content was something like <h1>Hello World!</h1>
, applying the safe
filter would render that value into the DOM verbatim, instead of HTML escaping it to <h1>Hello World!</h1>
.
Some filters require arguments above and beyond the value we're applying the filter to, such as the default
filter. The default
filter allows us to define a default value to use—other than an empty string—if the variable's value has not been set. As such, we need to provide the default filter with what we want that value to be. We do this using a colon:
{{content|default:"This is some default crud."}}
This will render the value of content
, or if content
is not set, render This is some default crud
.
You can read a complete list of Selmer's built-in filters at https://github.com/yogthos/Selmer#built-in-filters-1.
Whereas variables live inside {{ }}
, tags live inside {% %}
. Selmer tags are something like commands or instructions. Some of them are a simple one line statement called inline tags, such as include
. Others contain a content body (which I'll refer to as content tags, for the purpose of avoiding ambiguity), such as if
and block
. All of them, however, contain some kind of expression with varying complexity, respective to the tag.
For example, the include
tag's expression is the absolute path to the file we want to include at that location in the page:
{% include "templates/some-other-template.html" %}
Comparatively, the if
tag requires an expression of truthiness and a content block:
{% if 5 > 4 %} <h2>Newsflash!</h2> <p>Five is always bigger than 4.</p> {% endif %}
In this scenario, the content block (HTML fragment) will only be rendered if the expression in the if
tag evaluates to true
(which it always is in our case).
Selmer has just over a dozen different tags available at your disposal, which you can read at https://github.com/yogthos/Selmer#built-in-tags-1.
Like many templating libraries, Selmer allows a form of template inheritance. Templates can extend other templates through the use of block
tags, which define a content body that can be overwritten by child templates. If we open the base.html
template (/resources/templates/base.html
), we see the following snippet near the middle of page:
<div class="container"> {% block content %} {% endblock %} </div>
Here, we've defined a block called content
, but without any copy. The idea being that any template that inherits this template can populate this block's copy by defining block
with the same name in the child. For example, say we had the following lines of code:
<!-- parent.html --> <div class="example"> {% block example-content %} {% endblock %} </div> <!-- child.html --> {% extends "parent.html" %} {% block example-content %} Press the button to get the party started. <button>Start Party</button> {% endblock %}
If we were to render child.html
, the actual output would be:
<div class="example"> Press the button to get the party started <button>Start Party</button> </div>
However, if we were to render parent.html
, the content would be empty:
<div class="example"> </div>
18.191.234.150