Invenzzia » Resources / Articles / Managing XML in OPT / Managing attributes

5. Managing attributes

As we can create a basic template structure, we are ready to learn how to generate the attributes and their values dynamically. Everyone who used to write templates in PHP or Smarty should remember that writing a proper algorithm to display an attribute depending on certain conditions led to create an unreadable, messy code. In Open Power Template we do not suffer from that, but we must change the way we think about attributes. First of all, we remind that the template must be a correct XML document and we must not write something like this:

<div $class>
 
</div>

Moreover, we must not write both <div class="$variable"> and <div class="{$variable}">. Such mistakes are very common among the new OPT users who used to write in Smarty. Everything will become clear once you understand some simple rules. To clarify everything - every code like $variable, $a+$b or function($variable) is called expression. The curly brackets we can see in the static text

<p>{$zmienna}</p>

are not a part of expression, but a sign for compiler: 'right in this place you must display the result of evaluating this expression'. If we put an expression in an attribute, we do not write them, because now the expression is enclosed by the attribute quotes. By default, OPT prints the encountered value of an attribute: the code <div class="$variable"> gives us the output <div class="$variable"> because we have not informed the compiler that we expect a dynamic value here. In order to make such a notification, we must use the special namespace parse:

<div parse:class="$variable">
  ...
</div>

Now the compiler will parse it correctly and read the attribute value from a variable $variable. The parse namespace can be also used with some instruction attributes. Such cases are mentioned in the manual.

The way OPT handles dynamic attribute values causes some problems, if the attribute already belongs to a namespace. This problem will be fixed in OPT 2.1, where the new syntax will be introduced: <div class="parse:$variable">. You do not have to bother with syntax changes, as the new version will provide both the compatibility mode and the automatic template converter.

Dynamically calculated values may contain some HTML code that may potentially destroy our template structure. However, OPT pays attention to it and the attribute values are automatically escaped. By default, the same process is applied to the static text expressions, too. The HTML escaping process can be controlled at the compiler, template and expression level.

So far we know how to read an attribute value from an expression, but what to do, if the whole attribute must be displayed conditionally? For more advanced manipulations OPT provides another instruction: opt:attribute which creates an attribute with the specified name and value in the parent element. Below we can see a sample code that marks certain topic on a discussion board by adding an appropriate CSS class to it:

<opt:section name="topics">
<tr>
  <opt:attribute str:name="class" str:value="marked" opt:if="$topics.marked" />
 
  <td> ... </td>
  <td> ... </td>
  ...
</tr>
</opt:section>

opt:attribute accepts other OPT instructions in their attribute form, such as opt:if (conditional attribute displaying) or opt:section (loading a list of attributes from a section). By the way, we can observe what has been told a bit earlier. The extra namespaces can be used with certain instruction attributes. Here, the instruction expects the expression as the value of attributes name and value. However, in order not to write a code like name="'class'" with duplicated quotes, the compiler provides a more convenient form: the str: namespace which tells us: "you expect an expression here, but I want this expression to be a static text". Sometimes the situation is different - the compiler expects a string, but we want to load it from a variable. Then we use parse:.

But let's get back to our opt:instruction attribute. If we want to select the value conditionally, OPT provides us another trick:

<opt:section name="topics">
<tr>
  <opt:attribute str:name="class">
    <opt:value test="$topics.sticky">sticky</opt:value>
    <opt:value test="$topics.announcement">announcement</opt:value>
    <opt:value test="$topics.important">important</opt:value>
    <opt:value test="$topics.hot">hot</opt:value>
  </opt:attribute>
 
  <td> ... </td>
  <td> ... </td>
  ...
</tr>
</opt:section>

To define a default value, if neither of the conditions is passed, we either add an valueattribute to the instruction or put the opt:value tag without a condition defined.

In order to construct a dynamic attribute list from a container, we do not have to enclose opt:attribute within complex clauses with sections etc. All we need are two special attributes: opt:attributes-build and opt:attributes-ignore that load the attribute list from a container. The second one allows us to define the attribute names that needs to be ignored (an array or a string with attribute names separated with commas).

<textarea opt:attributes-build="$attributeList" opt:attributes-ignore="class, id">
  Text
</textarea>

The sample code above allows us to create an attribute list for the <textarea> tag. We are guaranteed that the programmers are not able to add any class and id attributes which could break the layout. Below, we can see a sample data for the template:

$view->attributeList = array(
  'rows' => 50,
  'cols' => 30,
  'class' => 'foo'  // this attribute will be ignored and skipped
);

There is one more useful trick. Sometimes we want to add a dynamic attribute to a single tag, but opt:attribute forces us to add some content to it. We must force OPT to ignore the white characters and print us a single tag inspite of what it sees in the code. This can be done in the following way:

<hr opt:single="yes">
  <opt:attribute str:name="class" str:value="foo" opt:if="$someCondition" />
</hr>

Thanks to opt:single our <hr> tag always remains printed as a single tag <hr /> with an optional class attribute.

As we can see, despite some advanced functionality and different approach, OPT tries to make the template code clear and readable.