-
Notifications
You must be signed in to change notification settings - Fork 279
Quickstart: HTML Templates
Jump to:
HTML templates are definitions of page views that get populated with relevant information. PencilBlue uses the common Model/View/Controller structure, where templates are loaded by page controllers (application logic files) and modified by information from the database, before being sent to the user's browser as a completed page.
On top of traditional MVC structure, PencilBlue HTML templates also utilize a hierarchical loading pattern, where plugins override core templates. They also use both static and configurable directives that allow the injection of complicated data outputs.
The PencilBlue template service, located at /include/service/entities/template_service.js, loads HTML templates into controllers and overwrites directives with the proper content. If we want to load a template located at /templates/admin/index.html and output its raw content, we would use the following code in a controller:
module.exports = function(pb) {
//PB dependencies
var util = pb.util;
var BaseController = pb.BaseController;
// Instantiate the controller & inherit from the PencilBlue base controller
function AdminIndexController(){};
util.inherits(AdminIndexController, BaseController);
// The render function is called by the router to render the page.
AdminIndexController.prototype.render = function(cb) {
// The template service is located at this.ts in BaseController
this.setPageName('Admin Home');
this.ts.load('admin/index', function(error, result) {
// Call the callback function with content set to the result of the template loading
cb({content: result});
});
};
return AdminIndexController;
};The previous code does the following:
- Creates a controller called AdminIndexController.
- Extends the controller with the PencilBlue base controller, which contains standard controller elements, including an object instance of the template service.
- In the render function, tells the template service to load the admin/index template and receives the result in a callback function.
- Uses the render function's callback function to tell PencilBlue that the content to render is the result from the template service load.
HTML templates can contain several globally accessible directives that will automatically add dynamic content to pages. There are three major global directives:
-
^tmp_[subtemplate]^: subloads a template -
^loc_[localizaton_key]^: loads localized text -
^analytics^: loads code for installed analytics providers
Using all three, the admin/index template might look like this:
<!-- Loads template at admin/head.html -->
^tmp_admin=head^
<div class="container">
<!-- Retrieves the HELLO_WORLD localized text -->
^loc_HELLO_WORLD^
</div>
<!-- Loads analytics code -->
^analytics^
<!-- Loads template at admin/footer.html -->
^tmp_admin=footer^After being loaded through the template service, output of this page might look like this:
<html>
<head>
<title>My Site</title>
</head>
<body>
<div class="container">
Hello World
</div>
<script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','//www.google-analytics.com/analytics.js','ga');ga('create', 'UA-1234567-8', 'mysite.com');ga('send', 'pageview');</script>
</body>
</html>Sometimes you'll want to create your own directives for injecting custom data into pages. PencilBlue makes this easy through the template service's registerLocal function.
To add a custom directive to a HTML template, first wrap a custom directive name in up carets (^):
^tmp_admin=head^
<div class="container">
<!-- Add a custom directive to inject a user's first name -->
^loc_HELLO^ ^user_name^
</div>
^analytics^
^tmp_admin=footer^Then call the registerLocal function before the template service's load function:
AdminIndexController.prototype.render = function(cb) {
this.ts.registerLocal('user_name', this.session.authentication.user.first_name);
this.ts.load('admin/index', function(error, result) {
cb({content: result});
});
};In this example, the template service will automatically replace ^user_name^ with the currently logged in user's first name. For more complicated replacements, we can define a function to calculate the right string to overwrite the directive with:
AdminIndexController.prototype.render = function(cb) {
this.ts.registerLocal('user_name', function(flag, cb) {
var randomInt = parseInt(Math.random() * 100);
cb(null, randomInt < 50 ? 'Dude' : 'Chico');
});
this.ts.load('admin/index', function(error, result) {
cb({content: result});
});
};In this example, whenever the template service runs into ^user_name^ it will generate a random number and return a name based on the value.
Sometimes it is necessary to pass pre-formatted HTML to the template service through a directive. By default, the template service encode the values that are passed back. To instruct the template service not to encode a value you must wrap the value in a TemplateValue. The documentation for the prototype can be found here.
AdminIndexController.prototype.render = function(cb) {
this.ts.registerLocal('sample_code', function(flag, cb) {
//the function is serialized so it can be viewed as HTML
var exampleFunction = function(i) {
return i + 10;
};
var content = '<pre>' + exampleFunction.toString() + '</pre>';
cb(null, new pb.TemplateValue(content, false));
});
this.ts.load('admin/index', function(error, result) {
cb({content: result});
});
};The above example registers a custom directive sample_code that creates a function that is serialized and passed back to the template service. The serialized function is wrapped in a pre tag to preserve formatting when viewed in the browser. In the callback, we wrap the generated HTML snippet in a TemplateValue and specify false as the second parameter so that the template service knows not to HTML encode the value. It is considered trusted content.
PencilBlue utilizes a methodology of template loading by which plugins can easily override core template files. This means that a plugin can be written that consists of nothing more than a HTML template meant to override the layout of a page, by matching the core template's file structure. PencilBlue prioritizes templates in the following order:
- The template exists in the active theme
- The template exists in a plugin
- The template exists in the core code of PencilBlue
For example, in order to override the admin/index.html template, you must install a plugin that has its own template under admin/index.html. The full path of that template could be /plugins/my_plugin/templates/admin/index.html.
If the active theme of the site also has a template under admin/index.html then it will take priority over your plugin's template.
You can see this type of overriding in the Portfolio Theme where the new and edit page screens are overridden to allow for the inclusion of a hero image field.