bengillies.net

a blog by Ben Gillies

How to make a Template in TiddlyWebPages

Since I announced TiddlyWebPages, I've been meaning to write a quick tutorial explaining how to make templates and define custom URLs for use on your site. In this post, I'll just cover templates and save URLs for another day. So let's start off at the point where you've installed TiddlyWeb, you have some content in your store, and you've installed TiddlyWebPages.

Right, the first step is to create a bag called templates and a bag called urls. Within the templates bag, you'll need to create a "wrapper" template. This will be the wrapper for all your content, so you can add links to your CSS definitions and Javascript files (or anything else) here without needing to include such things in every template. It needs to be called "Default" as it's the first one (and you need some sort of default right?) and while you can add other wrappers, we're not going into that quite yet. For now though, get the Default tiddler in the TiddlyWebPages GitHub directory at http://github.com/bengillies/TiddlyWeb-Plugins/raw/master/tw_pages/Default.tid and put it into your templates bag. You'll notice a couple of extra fields. list_tiddlers lets you specify the default template to use with a list of tiddlers (be it a bag, recipe or search results) while single_tiddlers does the same thing but for displaying a single tiddler only.

Your First Template


Now that's done, you can start adding templates. Now, as templates are all tiddlers themselves, you may want to just copy some from somebody else (my templates for example), but as this post is entitled "How to make a template", we'll assume you want to start from scratch.

Create a tiddler in the templates bag entitled "List". This will simply list all tiddlers in the bag/recipe that you are in, showing both the title, and the text. Next, enter the following information into the body of the tiddler:

{% for tiddler in base %}
<h1>{{tiddler.title}}</h1>
{{tiddler.text}}
{% endfor %}


Let's take a look at that line by line shall we?
{% for tiddler in base %}

This line starts a loop over all tiddlers, giving each tiddler in the list (called base, short for "base tiddlers") a name that you can refer to it with of "tiddler". "base" is a value that TiddlyWebPages provides you with to access the base set of tiddlers in the bag/recipe.

You can add other tiddlers, pre-formatted with another template, by specifying the template/recipe combination as a field in the template you want to include it in. For example, let's say you have created a "Header" template already and want to include it in your "List" template. Let's say that the tiddler that defines your header, is returned by the "my_header" recipe. To do that, you'd add a field to the "List" template specifying the "Header" template and the "my_header" recipe:
Header: my_recipe?select=title:ListHeading

In this definition, "Header" is the title of the field, and specifies the template you want to use, while "my_recipe" specifies the recipe that you want to use. Anything after the question mark is run as a filter on the recipe. In this case, we are selecting the ListHeading tiddler from the recipe. The ability to filter recipes is especially useful for sorting tiddlers, as recipes cannot sort by default.

Adding this Header into the template, would produce a result as follows:
{{extra['Header']}}
{% for tiddler in base %}
<h1>{{tiddler.title}}</h1>
{{tiddler.text}}
{% endfor %}

Notice the new variable called "extra". This contains the HTML for any extra templates you have specified in the tiddler's "fields" (as above). This gives two main variables that you can use: base; and extra. Other variables are: prefix - any server prefix you have defined in tiddlywebconfig.py; query - a key/value list of all items in the query string of the url; root_vars - a key/value list of any patterns that you have specified in your URL (by default, this will contain the tiddler name, in root_vars['tiddler'], the recipe name in root_vars['recipe'] and the bag name in root_vars['bag']).
<h1>{{tiddler.title}}</h1>

You can put HTML wherever you like, which can form the basis of your template. The tiddler.title is enclosed in double braces - this tells TiddlyWebPages (actually the Jinja2 templating engine) that tiddler.title is a variable and you want to write its value into the page. The {% ... %} pattern tells Jinja2 that you want to run something without writing the output to the page. In the example above, this would be the for loop.

{{tiddler.text}}
{% endfor %}

These lines just output the text of the tiddler and close the for loop. Optionally, you can choose to wikify the text by passing it through a filter and specifying a recipe name to use when creating links. If you did this with the recipe "sitecontent" (the recipe on my site that contains all my tiddlers) then you'd end up with the following:
{{tiddler.text|wikifier('sitecontent')}}

In addition to all the standard Jinja filters and the wikifier, there is another filter called "shorten" that you can use to safely shorten HTML without worrying about cutting off closing tags.

Now that you've done that, you'll want to test it. So go to the tiddlers in one of your bags, and append ".List" to the end of tiddlers to see the result. You can try this on my site by going to http://bengillies.net/.a/recipes/blog/tiddlers.ContentList (my template is a bit more complicated though).


Finally, please read through the Jinja2 documentation to get a better idea of what you can accomplish. You can find it at http://jinja.pocoo.org/2/documentation/templates

You can also view my Templates at http://bengillies.net/.a/bags/templates/tiddlers If you want to use any of them yourself.

Comments

name:
comment:
Just wondering why TiddlyWebPages requires 2 bags - one for urls and one for templates. Could they not be one of the same?

I would be interested in a setup where you have one bag templates where the titles correspond to selector urls. I think this would lead to quite good design as then every template would have its own url (even subtemplates). Is there a good reason why 2 bags are necessary?
by Jon Robson
Hi Jon

At the moment, the reason that 2 bags are required is that there is no content type field that distinguishes between a url and a template, which would lead to much confusion. This is something I have thought about though, so may appear in a later version.

I'm not sure that naming them with a selector URL would be appropriate though as all templates can already be accessed in the same way as other serializations - just append the template name onto the end of the url. I like that the URLs are separate, as this allows them to operate in a similar manner to the standard TiddlyWeb URLs (applying filters, etc).
by Ben Gillies
My first impressions are "what the hecks going on". It seems that you are using field names that refer to templates?

So if I have a field called "Bar" that would reference a tiddler called "Bar" in the template bag. The value of that field provides the list of tiddlers.

Surely it would make sense to have a template have a standard field across all tiddlers eg. that field might be tiddlers.

I think selector urls are appropriate. As a developer with jinja2 and tiddlyweb, selectors are the first place I start. I add a new one then create a function for it and then typically replicate a lot of code. When I need to modify something I then typically might look in my selectors to work out which function I need to modify.

In the ILGA project ([[http://new.ilga.org]]) we are working on I can't see why this setup wouldn't work.

For instance on the articles page you would have a tiddler with title "/{language:segment}/Articles"

inside this tiddler you would have a field
tiddlers: /ilga/bags/published_articles_en?select=language:en

and then the text would be something like the template
{% extend 'commonlookandfeel' %}
{% for tid in tiddlers %}
{{tid.title}}
{% endfor%}

commonlookandfeel would be a tiddler which would have no tiddlers field and just provide a 'shell' for the articles page.

There is no reason I could then append extra filters to the template to filter the pages further.
by Jon Robson
I agree that in its present incarnation, it is overly complicated and could do with simplifying somewhat, how that should be done, I'm not sure though.

You are correct in that field names refer to templates. The value then refers to a recipe (with optional filter) which is then passed into the named template and HTML is generated.

The underlying structure is subtly different to how we have used Jinja in the past though, and is designed to make use of the TiddlyWeb bag/recipe structure.

I believe you're thinking about it in the wrong way, as in your example, you state that you need to add a new url, and then copy a lot of other code, and then start on a template. To give you the same example in TiddlyWebPages, you might do the following:

*you want articles separated by language, so create a recipe with the following: /bags/Articles/tiddlers?select=language{{ language:en }}
which would default to "en" should no other language be specified.

*you want this to appear at /{language:segment}/Articles, so create a url tiddler, the name of which is the title of the page eg - Articles ({{ language }})
the body of which would be /{language:segment}/Articles. You can then specify the recipe in the recipe field eg - recipe_name: language_articles?sort=-modified.

*you then need a template to load up at this url. Let's call it summary. So in the url tiddler, specify - template: summary

*you can then create a summary template, that can be reused anywhere:

{% for tiddler in base %}
{{tiddler.title}}
{% endfor %}

The benefit of this way over other ways is that, should you want a "tags" page, instead of creating anything new, you could just visit:

/bags/Articles/tiddlers.summary?select=tags:tag_name

I should add here that if you weren't bothered about the custom URL, you could just skip all but the last step here, just creating the template and visiting the appropriate URL.

Alternatively, if you wanted to include this page inside another one, you'd simply add "summary: language_recipe" to the new template tiddler's fields, and then put {{extra['summary']}} somewhere in the template.

I'm quite happy to discuss this further as there are clearly a few things that could be simplified, but I'm not sure the comments section is the best place to do it.
by Ben Gillies