Dynamic CSS/JS loading
From Invenzzia wiki
In this short article we would like to show a nice trick about defining the CSS style list with OPT. Suppose that in our web application the actions may select their own CSS files they want to use. By default, there should be loaded the main style, but an action with AJAX form may request also an extra style to render the form cells, whereas the module to generate the printable content needs a completely different stylesheet. It can be easily achieved with OPT.
Contents |
Basic version
Let's begin with the initial main template:
<?xml version="1.0" ?>
<opt:root>
<opt:prolog />
<opt:dtd template="xhtml10transitional" />
<html>
<head>
<title>{$website.title}</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="main.css" type="text/css" media="ALL"/>
</head>
<body>
<div id="header">
<h1>My website</h1>
</div>
<div id="content">
<opt:section name="actions">
<opt:include from="actions" />
</opt:section>
</div>
<div id="footer">
<p>Copyright notes etc.</p>
</div>
</body>
</html>
</opt:root>
The CSS style is currently hard-coded in the template. In order to allow a dynamic selection, we may use sections:
<opt:section name="css">
<link rel="stylesheet" parse:href="$css.stylesheet" type="text/css" parse:media="$css.media"/>
</opt:section>
Now the script can generate a list of requested stylesheets and upload it into the layout view:
$view = new Opt_View('layout.tpl');
$view->css = array(0 =>
array('stylesheet' => 'main.css', 'media' => 'ALL'),
array('stylesheet' => 'agenda.css', 'media' => 'ALL'),
array('stylesheet' => 'widgets.css', 'media' => 'ALL')
);
$output = new Opt_Output_Http;
$output->render($view);
It is easy and flexible enough for most cases. You may try to use opt:sectionelse to specify the default stylesheets:
<opt:section name="css">
<link rel="stylesheet" parse:href="$css.stylesheet" type="text/css" parse:media="$css.media"/>
<opt:sectionelse>
<link rel="stylesheet" href="main.css" type="text/css" media="ALL"/>
</opt:sectionelse>
</opt:section>
Advanced version
The basic version should be enough for most people, but this is not all in this topic. Sometimes, we have a group of CSS files that must be loaded together, because all of them describe the overall look of the same feature. In the previous example, the action that wants to use them, must specify their names manually. However, in certain cases we may encode such predefined choices directly in the template, letting the action to select one or more predefined groups. It can be achieved with opt:selector instruction, another kind of sections:
<opt:selector name="css">
<opt:general>
<link rel="stylesheet" href="main.css" type="text/css" media="ALL" />
<link rel="stylesheet" href="intranet.css" type="text/css" media="ALL" />
</opt:general>
<opt:extjs>
<link rel="stylesheet" href="js/ext/resources/css/ext-all.css" type="text/css" />
</opt:extjs>
<opt:printable>
<link rel="stylesheet" href="printable.css" type="text/css" media="print"/>
</opt:printable>
<opt:default>
<link rel="stylesheet" parse:href="$css.stylesheet" type="text/css" parse:media="$css.media"/>
</opt:default>
</opt:selector>
opt:selector is a combination of a section (smart loop) with a switch statement from programming languages. For each list item, it selects one of the available content variants and renders it. In the PHP code, we use it similarly to the previous example. The main difference is the field item that contains the name of the requested content variant.
$view = new Opt_View('layout.tpl');
$view->css = array(0 =>
array('item' => 'general'), // load the styles in the "general" group.
array('item' => 'printable'), // load the CSS files in the "printable" group.
array('item' => 'default', 'stylesheet' => 'custom', 'media' => 'ALL') // add a custom CSS file.
);
$output = new Opt_Output_Http;
$output->render($view);
As you see, in the advanced version the most commonly used CSS file groups are hard-coded in the template with the availability of selecting the necessary ones, but moreover - the action may still load an extra custom CSS file.
Reusing the code
If the contents of the opt:selector need to be reused across many templates, we do not have to create multiple copies of the same piece of code. The snippets will help us a lot. Let's define an extra template named snippets.tpl:
<?xml version="1.0" ?>
<opt:root>
<opt:snippet name="cssSelector">
<opt:general>
<link rel="stylesheet" href="main.css" type="text/css" media="ALL" />
<link rel="stylesheet" href="intranet.css" type="text/css" media="ALL" />
</opt:general>
<opt:extjs>
<link rel="stylesheet" href="js/ext/resources/css/ext-all.css" type="text/css" />
</opt:extjs>
<opt:printable>
<link rel="stylesheet" href="printable.css" type="text/css" media="print"/>
</opt:printable>
<opt:default>
<link rel="stylesheet" parse:href="$cssSelector.stylesheet" type="text/css" parse:media="$cssSelector.media"/>
</opt:default>
</opt:snippet>
</opt:root>
We may load it into the layout templates that need this list:
<?xml version="1.0" ?>
<opt:root include="snippets.tpl">
<opt:prolog />
<opt:dtd template="xhtml10transitional" />
<html>
<head>
<title>{$website.title}</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<opt:selector name="css" opt:use="cssSelector" />
</head>
....
</html>
</opt:root>
The PHP code does not need to be changed. opt:use pastes the contents of the cssSelector snippet as a content for opt:selector. Moreover, OPT automatically links the variables $cssSelector.stylesheet to point to the section variables. Thus, the CSS list may be reused in more than one template.
Conclusion
This example shows that we do not need any advanced object-oriented programming solutions just to generate a list of CSS files for our website. We have achieved everything, using some basic OPT template language syntax features, simply declaring, what we want to see, not - how it is supposed to work. Notice that the template is not even dependent on any particular PHP data structure. The list of CSS files may be an array, object... we can even write a data format that reads the CSS list directly from the web application framework API. Similar solutions can be used with loading the JavaScript files.

