Skip to content
Ryan Neufeld edited this page Jul 9, 2013 · 9 revisions

To follow along with this section, start with tag v2.0.9.

Template HTML can be created in many different ways. You could write it, generate it or download it from the Internet. However you get it, you will have some HTML that you would like to use in your pedestal-app application.

Pedestal-app does not enforce how templates are made. A single HTML file can contain one template or hundreds of them. How template HTML is organized is organized will depend on what makes sense for a project.

In this section you will convert template HTML from a string into a something which can be used from your application. While doing this you will be introduced to the following pedestal-app concepts:

  • Static vs Dynamic Templates
  • Template Slicing

Slicing Templates

Pedestal-app provides a very flexible way to write template source files. In order to use these templates in your application, you need to extract them from the source.

The goal is to create a map, which is available to the ClojureScript program, which contains all of the templates that you will use. Pedestal-app provides functions to help you create this map and extract templates from source files.

This work of "slicing" templates is done in the file

tutorial-client/app/src/tutorial_client/html_templates.clj

which currently contains the generated code shown below.

(defmacro tutorial-client-templates
  []
  {:tutorial-client-page (dtfn (tnodes "tutorial-client.html" "tutorial" [[:#other-counters]])
                               #{:id})
   :other-counter (dtfn (tnodes "tutorial-client.html" "other-counter") #{:id})})

This is a Clojure macro but you can think of it as a normal Clojure function. The only thing that is different about this function is that it will be run at compile time. When ClojureScript code is compiled, any call to tutorial-client-templates will be replaced with the return value of this function.

This code uses functions form the io.pedestal.app.templates namespace to extract templates and create useful functions.

The tnodes function converts a template in a file into a sequence of Enlive nodes.

(tnodes "tutorial-client.html" "other-counter")

The third argument to tnodes is a vector in Enlive selectors that you would like to have "cleared". This means that the contents of the these nodes will not be included. This is helpful when you have some example data in the HTML that you don't want in the actual template, as is the case below, where you do not what to include an inner template in an outer one.

(tnodes "tutorial-client.html" "tutorial" [[:#other-counters]])

Because this is a sequence of Enlive nodes, you can perform any other transformations that you would like using the powerful Enlive Clojure library.

There are two ways to turn this data into a template. You could create a static template with tfn or a dynamic template with dtfn.

tfn is short for template function and will create a function which, when passed a map, will fill in the template with the values in the map and return a string of HTML which can be added to the DOM. Use this when you don't need to update the template after it has been added to the DOM.

dtfn is short for dynamic template function and will create a template which can be updated even after it has been added to the DOM. dtfn takes two arguments, the sequence of nodes and a set of static fields. Static fields can be set once when the template is first added to the DOM, but they cannot be changed later. In the example above the ids are static fields.

Checking Templates from the REPL

When doing a lot of template slicing, it can be helpful to confirm that they work form the REPL before trying to use them in your application. In the tutorial-client.html-templates namespace, try some of the following:

Create the template map.

(def t (tutorial-client-templates))

Run the dynamic template function.

(def tf (:other-counter t))
(tf)

This will return a tuple like the one shown below.

[{:counter-id [{:id G__1866, :type :content}],
  :count [{:id G__1865, :type :content}]}
 #<html_templates$fn__1857$fn__1858 tutorial_client.html_templates$fn__1857$fn__1858@5669e732>]

The first element is a map of dynamic fields to the ids and types of things they map to. In the above example, :count maps to the content of an element with id G__1865. The second element is a function which will generate the HTML which should be added to the DOM. When this function is called, it should be passed a map containing data to populate the template.

The code below can be used to try out the dynamic template function.

(let [[_ ctor] (tf)]
  (ctor {:id 42}))

Executing this code will generate the HTML below.

<div id="42" class="counter-row">
  <span class="counter-label">Counter for <span id="G__1878"></span>:</span>
  <span id="G__1877" class="counter"></span>
</div>

Dynamic templates are hard to work with manually. In the Rendering section you will see how much of this work is done for you with the help some functions from the io.pedestal.app.render.push.templates namespace.

Please note that any time you make changes to the namespace tutorial-client.html-templates the server will have to be restarted or this code reloaded in order for the changes to take effect.

Next steps

In the next section you will write the rendering code which uses these templates to render the application.

The tag for this section is v2.0.10.

Home | Rendering

Clone this wiki locally