Invenzzia » Resources / Articles / OPT tips and tricks

OPT tips and tricks

  • Published on 1 April 2009 07:30:00 GMT, updated over 5 years ago by Tomasz Jędrzejewski

Here you can find a nice set of practical tips and tricks shortly explaining, how to solve particular problems and how to use different features of Open Power Template 2.

  1. How can I make a dynamic HTML attribute value?
  2. How can I add a conditional attribute?
  3. How can I make a valid XML template?
  4. How can I modularize my templates?
  5. How to display, for example categories and the elements assigned to them?
  6. How to remove the list header, if there are no data assigned to the section?
  7. The default relationship for nested sections does not suit me. How can I change it?
  8. How to create a breadcrumb for my website?
  9. How to escape the Javascript code in the output that contains some dynamic code?
  10. How to display the list in columns?
  11. How to create a multilingual website?
  12. How can I make a dynamic template inheritance?
  13. How can I work with HTML forms in OPT?
  14. Does OPT have something similar to components but with simpler functionality?

1. How can I make a dynamic HTML attribute value?

As you know, in XML mode OPT parses the HTML attributes, too. By default, their value is treaded as a static text, however - you cannot use curly brackets there:

<!-- WRONG!!! -->
<div class="{$cssClass}"></div>

Instead, you must move the attribute to the parse namespace and its value will be evaluated as an OPT expression:

<!-- Good -->
<div parse:class="$cssClass"></div>

Note that if you are going to combine a variable value with some static text, you have to write it as an expression:

<!-- Good -->
<div parse:class="'some static text' ~ $cssClass"></div>

The rules for constructing the expressions are very similar to PHP language. There are mostly minor differences, for example the concatenation operator is ~, because . is reserved for other purpose.

2. How can I add a conditional attribute?

Use opt:attribute instruction:

<div>
    <opt:attribute name="$attributeName" value="$attributeValue" opt:if="$displayMe" />
</div>

This allows to create a dynamic HTML attribute with the name defined by the variable $attributeName and value taken from $attributeValue. The variable $displayMe decides whether the attribute will be shown. Note that you can also combine opt:attribute with sections to create a dynamically changed list of tag attributes.

3. How can I make a valid XML template?

By default, OPT tries to achieve the full XML standard compliance (it is called the "XML mode"). The template prolog and DTD are for OPT use only, moreover - you have to provide maximum one root element. In order to produce the XML prologs and DTD for the output, you can use three instructions:

  1. opt:prolog
  2. opt:dtd
  3. opt:root

A sample XML-mode template:

<?xml version="1.0" ?>
<opt:root>
<opt:prolog version="1.0" /> <!-- generate a prolog for the output -->
<opt:dtd template="xhtml10transitional" /> <!-- generate a doctype for the output -->
<html>
... 
</html>
</opt:root>

If you do not like such code, you can always switch OPT to the HTML mode by disabling some options in the configuration:

$tpl = new Opt_Class;
$tpl->prologRequired = false;  // Now the templates do not have to have prolog and if it is found, it is sent to the browser.
$tpl->singleRootNode = false;  // There could be more than one root element in the template.
$tpl->htmlAttributes = true;   // Allows the HTML-style attributes: <tag attribute>

4. How can I modularize my templates?

OPT supports two ways to modularize the templates:

  1. Template inclusion with opt:include instruction.
  2. Template inheritance.

They can be used together and even should, because opt:include works during the execution time, and the inheritance is solved on the compilation stage. The details can be found here.

5. How to display, for example categories and the elements assigned to them?

Use two nested sections. OPT will automatically recognize that you want to create a relationship between them:

<h1>Category list</h1>
<opt:section name="categories">
    <div class="category">
        <h3>{$categories.name}</h3>
        <p>The elements assigned to this category:</p>
 
        <opt:section name="elements">
        <div class="element">
            <h4>{$element.name}</h4>
            <p>{$element.description}</h4>
        </div>
        </opt:section>  
    </div>
</opt:section>

Note that this code does not contain any information on the data format you are going to display. It can be managed entirely on the script side:

$view = new Opt_View('my_template.tpl');
 
$view->categories = array(0 =>
    array('name' => 'Category 1'),
    array('name' => 'Category 2'),
);
 
$view->elements = array(0 =>
    // Elements for the first category
    array(0 =>
        array('name' => 'Element 1', 'description' => 'Some description'),
        array('name' => 'Element 2', 'description' => 'Some description'),
    ),
 
    // Elements for the first category
    array(0 =>
        array('name' => 'Element 3', 'description' => 'Some description'),
        array('name' => 'Element 4', 'description' => 'Some description'),
    )
);
 
$out = new Opt_Output_Http;
$out->render($view);

If you do not like such data format, you may change it with Opt_View::setFormat() method to one of the predefined ones or to write your own format.

6. How to remove the list header, if there are no data assigned to the section?

Use opt:show instruction:

<opt:show name="list">
<ol>
    <opt:section>
    <li>{$list.something}</li>
    </opt:section>
</ol>
</opt:show>

Note that the section attributes are now defined in opt:show and opt:section remains empty.

You can also define an extra message:

<opt:show name="list">
    <ol>
        <opt:section>
        <li>{$list.something}</li>
        </opt:section>
    </ol>
    <opt:showelse><p>Sorry, no elements found</p></opt:showelse>
</opt:show>

7. The default relationship for nested sections does not suit me. How can I change it?

Use the parent attribute to specify the parent section manually. If you do not want any relationship, keep this attribute empty:

<!-- situation 1 -->
<opt:section name="top">
 
    <opt:section name="middle">  <!-- one-to-many relationship with "top" -->
 
        <opt:section name="bottom" parent="top">  <!-- one-to-many relationship with "top", not "middle" -->
        ...
        </opt:section>
 
    </opt:section>
 
</opt:section>
 
<!-- situation 2 -->
<opt:section name="top">
 
    <opt:section name="middle">  <!-- one-to-many relationship with "top" -->
 
        <opt:section name="bottom" parent="">  <!-- no relationship to parent sections. -->
        ...
        </opt:section>
 
    </opt:section>
 
</opt:section>

8. How to create a breadcrumb for my website?

A lot of websites contain a breadcrumb like this:

Home Page / Articles / Category / Some article / Comments

It is very easy to create such navigation item in OPT with sections and separators:

<p><opt:section name="navigation" str:separator=" / "><a parse:href="$navigation.address">{$navigation.name}</a></opt:section></p>

Now you have to populate the section with the list of the breadcrumb elements and OPT will do the rest.

9. How to escape the Javascript code in the output that contains some dynamic code?

The OPT XML parser recognizes and parses the CDATA sections, but in this particular case the default XML semantics is not useful for us - the following code will surely escape the Javascript, but also will produce an unparseable output:

Template:
 
<script type="text/javascript">
<![CDATA[
    document.write('Hello my friend, do you need {$object} or ]]>{$object}<![CDATA[?');
]]>
</script>
 
Invalid output:
 
<script type="text/javascript">
<opt:literal>
<![CDATA[
    document.write('Hello my friend, do you need {$object} or ]]>sunglasses<![CDATA[?');
]]>
</opt:literal>
</script>

Fortunately, OPT contains the opt:literal that allows the intelligent CDATA parsing:

Template:
 
<script type="text/javascript">
<opt:literal type="comment_cdata">
<![CDATA[
    document.write('Hello my friend, do you need {$object} or ]]>{$object}<![CDATA[?');
]]>
</opt:literal>
</script>
 
Correct output:
 
<script type="text/javascript">
/* <![CDATA[ */
    document.write('Hello my friend, do you need {$object} or sunglasses?');
/* ]]> */
</script>

10. How to display the list in columns?

Use one of section instructions: opt:grid:

<table>
<opt:grid name="gallery" columns="3">
    <tr>
        <opt:item>
            <td><img parse:src="$gallery.picture"/></td>
        </opt:item>
        <opt:emptyItem>
            <td> </td>
        </opt:emptyItem> 
    </tr>
</opt:grid>
</table>

This code will display pictures in three columns and if the number of pictures is not a multiple of 3, there will be placed empty cells defined by opt:emptyItem.

11. How to create a multilingual website?

OPT expression language contains language variables. They refer directly to your script's translation interface which loads the messages according to the chosen language. You do not have to assign the messages to the view manually. Another way is to use the backtick strings. The details are described in the documentation.

12. How can I make a dynamic template inheritance?

Although the template inheritance is solved during the template compilation, OPT still provides the dynamic inheritance feature. You can make the inheritance dynamic, using branches:

<opt:extend file="base_template1.tpl" someBranch="base_template2.tpl">
    ...
</opt:extend>
$view = new Opt_View('template.tpl');
$view->setBranch('someBranch'); // now our template will extend "base_template2.tpl" instead of "base_template1.tpl"

Or by specifying the template name directly:

<opt:extend file="base_template1.tpl" dynamic="yes">
    ...
</opt:extend>
$view = new Opt_View('template.tpl');
$view->inherit('template.tpl', 'base_template2.tpl');

Do not worry about the compilation. OPT has its own tricks to deal with it.

13. How can I work with HTML forms in OPT?

Designing a HTML form in a template engine that offers just conditional instructions is a nightmare. Just take a look at a part of the form in one of my projects, where I used PHP as a template language:

<div class="odd<?php echo (!empty($errors['password']) ? ' error' : ''); ?>">
    <?php if(!empty($errors['password'])){ ?>
        <p class="error"><?php echo $errors['password']; ?></p>
    <?php } ?>
 
    <label for="form_password">Password</label>
 
    <?php echo form::password(array('name' => 'password', 'id'=>'form_password', 'value' => $form['password'], 'class'=>'inputText')); ?>
</div>

Now imagine that such form is built of 20 fields. In OPT, you can forget about it thanks to the components. A component is a PHP object with some logic that is plugged into a special port in the template which defines the layout. You can write components for the common HTML controls like input field or text area and put them in OPT. Using snippets, you can easily share the same component layout over all your forms:

<opt:snippet name="componentLayout">
    <com:div> <!-- the component may add new attributes to this DIV -->
        <span class="title"><label parse:for="$opt.component.id">{$opt.component.title}</label></p>
        <span class="field"><opt:display /></span> <!-- a component element -->
        <opt:onEvent name="error">
            <p class="error">An error occured: {$opt.component.error}</p>
        </opt:onEvent>   
    </com:div>
</opt:snippet>
 
<!-- let's say we have a opt:inputText components and want to create a form: -->
<opt:inputField template="componentLayout">
    <opt:set name="name" str:value="field1" />
    <opt:set name="title" str:value="Some field" />
</opt:inputField>
 
<opt:inputField template="componentLayout">
    <opt:set name="name" str:value="field2" />
    <opt:set name="title" str:value="Some other field" />
</opt:inputField>

Easy, isn't it? If you would like to make small changes to the component layout, you just modify one snippet and the changes are propagated all around the script once the templates are recompiled. Note that you can create a generic port and load the component objects from a section, making a real dynamic form generation monster:

<opt:section name="formComponents">
    <opt:component from="$formComponents.component" template="componentLayout" />
</opt:section>

14. Does OPT have something similar to components but with simpler functionality?

Yes - they are called blocks. They are also PHP objects connected to a port in a template, but have much simpler API and can be used in the places, where the components are too powerful. Take a look at the manual to get to know more.

Open Power Template 2.0 projects tips'n'tricks

This text is licensed under Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States license.

Add new comment

You are writing a reply to comment

  • Will not be published | Gravatar supported
  • Type here the name of our group.