<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/fenix-renderers.tld" prefix="fr" %>

The first situation: presenting the world

Warming up

As the use of renderers is made through a TagLib, the first thing you need to do is to declare the use of the fenix-renderers.tld description file. The following line does the trick:

<%@ taglib uri="/WEB-INF/fenix-renderers.tld" prefix="fr" %>

The TLD is somewhat documented and eclipse is able to show that documentation if you have the Web Standard Tools installed. Just copy the fenix-renderers.tld to the jsp/WEB-INF directory and possibly restart eclipse. From this moment you can use the beloved Ctrl+Space shortcut to auto-complete tags and see documentation.

Presenting an object

The first thing you would like to know is: How can I just present something and what can I present?

The last part first. You can present any object given that you have a renderer configured to handle that object type. Normally there is a renderer for the java.lang.Object type so every object is presentable.

Now for the first part. To present an object you can use the view tag. If you have Eclipse configured then you can read the documentation for the tag while using it. But the basic use of the tag is:

Code

<fr:view name="UserView" scope="session"/>

Result

As you can see, we are using the default presentation for the UserView object available in the session scope. If you ommit the scope attribute then the attribute with the given name will be searched in all scopes starting from the most specific, that is, page scope.

You can also see that the default presentation of a person is simply a link to it's details. More concretly, it's a link to an action that presents the person in the tabular layout. You can select the layout directly using the layout attribute of the tag.

Code

<fr:view name="UserView" property="person" layout="tabular"/>

Result

You may have notice the introduction of the property attribute. This works together with the name attribute to further select the target object. It's supposed to behave exactly as in other tags available from the Struts project, so you can use a syntax like

as long as all middle elements are not null.

As Fénix domain objects are persistent you have and additional way of refering to a domain object. You can specify the object internal id type directly in the tag. If you do this the object will be retrieved from the database and displayed as before. So to display the same person as in the last example you could write:

<fr:view oid="<%= personId %>" type="net.sourceforge.fenixedu.domain.Person" layout="tabular"/>

One thing you must notice. The type must be the same as specified in the DML. If you use an interface as the type you will get a ClassNotPersistenceCapableException.

Before schemas what about ...

... that user friendlly names that appear in the left side of the table?

Glad you ask. As you won't believe in magic I'm forced to tell you that there is a new resource bundle in town: RendererResources. The text that appears in the left side is fetched from this resource bundle or from the default resource bundle associated with the module, that is, in the last case the ApplicationResources is used. Labels are searched in the resources using a simple convention. Each row correspondes to a slot of the object. For example, nome is one of the person's slots because we have the getNome getter. When the label for a slot is needed we search the renderer's resources for a key using the following order:

  1. label.net.sourceforge.fenixedu.domain.Person.nome
  2. label.nome
  3. nome

If no key is present the programmatic name of the slot is shown. Note that slots may have more complex names like pais.code. This does not correspond directly to a getter of person but is treated the same way as before.

... all the other getters that a person has due to all the relations it mantains?

For those you will have to request their display explicitly and decide how they are presented. By default only the direct slots of the object are displayed. In the case of domain objects, like the person here, those are the ones present in the class definition of the DML.

And why is this so?

Well, direct slots already gain the semantic of "data" in the DML. Relations are more related to business logic and domain organization than to data. Each person is related to a country. when you present a person you most probably want to show the slot nome but do you wan't to present the country? You may wan't to present the country's code or country's name but not all the information of the country because then you would have to handle and the country's relations as well. Now consider an external person. Itself it has no direct slots, only relations. So when you present a person you want to present it's relation with an external institution, an external jury, or an external guider. The need is context specific so the decision should also be.

Ah! Other thing. A person has roles. Suppose that in the manager context you wan't to display the details of a role, for instance, the role PERSON. If it's relations were shown by default then the manager would get, among other things, a list will several thousand items: all the active persons in the database.

And now schemas for everybody

The last example shows a lot of information some of which we may not need. We can control this by introducing schemas. The simple vision of schemas is that they declare which slots we are interested for a particular type. If, for example, we want to shown only the name, username, and email of a person we could declare the following schema:

<schema name="person.simple-admin-info" type="net.sourceforge.fenixedu.domain.Person">
    <slot name="nome"/>
    <slot name="username"/>
    <slot name="email"/>
</schema>

Schemas are refered by name so the names must be unique. To refer to a schema you can use the schema attribute in the view tag. The target object will be shown using the given schema, that is, like if it only had the slots declared in the schema.

Code

<fr:view name="UserView" property="person" 
         layout="tabular" schema="person.simple-admin-info"/>

Result

Renderers can receive properties

So we now can present a person showing only the information we want and in a tabular layout as intended. But sometimes we need to ajust some detail in the we things are presented. One of the things that may vary with the context is the style tables and other information is shown. Nevertheless we could need other subtle changes.

Most of these changes can be done by passing properties to the renderer. Yes you can pass properties to the renderers. If a renderer has methods getFoo and setFoo then it supports a property named "foo".

Ok. Before I show you how to set renderers properties some things must be explained. When you use the attribute layout you are actually using a sort of syntactic sugar for a more complex, expanded version. The expanded version of the last example is this:

<fr:view name="UserView" property="person" schema="person.simple-admin-info">
    <fr:layout name="tabular">
    </fr:layout>
</fr:view>

That inner layout tag allows you to both select and configure the layout. To set renderer's properties you can user the tag property that i to be used inside the tag layout. The tag property can have two attributes: name and value. And now an example:

Code

<fr:view name="UserView" property="person" schema="person.simple-admin-info">
    <fr:layout name="tabular">
        <fr:property name="classes" value="style1"/>
        <fr:property name="columnClasses" value="listClasses,"/>
    </fr:layout>
</fr:view>

Result

In this example we are setting the classes and columnClasses properties of the renderer. As the renderer generates a table these properties are used to costumize the relevant parts of it. You could also specify the properties rowClasses and headerClasses. Note that the value of columnClasses is "listClasses,<blanck>" meaning that the first column as the specified class and the second has no class.

You can also ommit the value attribute. In this case the value of the property will be the body of the property tag. This can be used and is usefull for complex values that span over multiple lines. It is also usefull when combined with CDATA blocks. Note that you can use other tags inside the property tag. They will be evaluated and the resulting text is used as the property value. The following example is hipotetical as the renderer associated with the tabular layout for the Person domain object does not support the given property.

Code

<fr:view name="UserView" property="person" schema="person.simple-admin-info">
    <fr:layout name="tabular">
        <fr:property name="description">
            This is a description that includes <acronym title="Hypertext Markup Language">HTML</acronym>
            and the use of inner <html:link href="http://java.sun.com/products/jsp/syntax/1.2/syntaxref1211.html">TagLibs</html:link>.
        <fr:property/>
    </fr:layout>
</fr:view>

You must note that the body of the property is only evaluated once before any object is renderered so if you are presenting several objects you can't include logic in the property value and expect it to be evaluated when each object is being presented.

Schemas have so much more to be said

We saw the simple use of schemas: to limit the presented slots. But schemas can contain much more information for each slot. You can, for instance, indicate the layout, schema, a properties that will be used to present the value of a slot. This makes the renderers adopt a more recursive and flexible style.

The following example extends the schema previously defined and uses it as before:

Schemas

<schema name="person.simple-admin-info.extended" type="net.sourceforge.fenixedu.domain.Person">
    <slot name="nome"/>
    <slot name="username" layout="link"/>
    <slot name="email">
        <property name="link" value="true"/>
    </slot>
    <slot name="pais" schema="country.short" layout="values">
        <property name="htmlSeparator" value=" - "/>
    </slot>
</schema>

<schema name="country.short" type="net.sourceforge.fenixedu.domain.Country">
    <slot name="code"/>
    <slot name="nationality"/>
</schema>

Code

<fr:view name="UserView" property="person" schema="person.simple-admin-info.extended">
    <fr:layout name="tabular">
        <fr:property name="classes" value="style1"/>
        <fr:property name="columnClasses" value="listClasses,"/>
    </fr:layout>
</fr:view>

Result

We add new schemas but what about new layouts?

First, remember that a layout is a combination of a name, a renderer and a set of properties. Layouts are defined in the configuration file and may use existing renderes. Of course, some layouts seem to have a one-to-one mapping with a renderer but that's not necessary.

Lets use the last example to add two new layouts. First lets suppose that the type of table used — with all that nice colors and borders — is frequently used. We could use the well known Copy&Paste to reuse the layout and it's properties. The same for the inline presentation of the country, that is, presenting several values separated by a simple " - ".

So we could do some sort of refactoring and create a layouts to represents those patterns. How do we do this? We add new entries to the renderers' configuration. Here is an example of what can be added:

<renderer type="java.lang.Object" layout="nice-details-table" 
          class="pt.ist.fenixWebFramework.renderers.StandardObjectRenderer">
    <property name="classes" value="style1"/>
    <property name="columnClasses" value="listClasses,"/>
</renderer>
    
<renderer type="java.lang.Object" layout="values-dash" 
          class="pt.ist.fenixWebFramework.renderers.ValuesRenderer">
    <property name="htmlSeparator" value=" - "/>
</renderer>

Two new layous named nide-details-table and values-dash were defined. So now we can ommit the properties from the previous example and use the new names for the layout.

Schemas

(...)
    <slot name="pais" schema="country.short" layout="values-dash"/>
(...)

Code

<fr:view name="UserView" property="person" schema="person.simple-admin-info.extended" layout="nice-details-table"/>

Result

We can always use templates and all that we already knew

Suppose that you need to present a person in a very exotic manner. It would probably be much easier to create a JSP — probably with a powerfull web page designer — and simply introduce a couple of bean:write tags to display the information you need. But obviously you don't want to abandon the power provided by the renderers so there is a possibility to mix both approaches.

One of the predefined renderers is the TemplateRenderer. You can configure the template property of this renderer to refer one JSP. What the renderer does is setup the environment for the JSP and then delegating the presentation to it. You can consider that the JSP is included in the place were you used the view tag.

First, here is an example of how to delegate the presentation of a person to a template:

Code

<fr:view name="UserView" property="person">
    <fr:layout name="template">
        <fr:property name="template" value="/manager/renderers/template.jsp"/>
    </fr:layout>
</fr:view>

Result

As this is a little too much to write just to specify the template that should be used, the view tag supports an abbreviation for this case. In the same way that the layout attribute allows to ommit the inner fr:layout tag, the template attribute allows you to ommit all the inner tags.

<fr:view name="UserView" property="person" template="/manager/renderers/template.jsp">

But how do we access the object to be presented from the template?

There is an additional taglib that can only be used in templates: fenix-template.tld. This taglib provides tags very similar to those we have been using but reference the object to be presented implicitly. Let's look at the template used before. You can easily see how each element was generated by looking at the (n) notation.

<%@ taglib uri="/WEB-INF/fenix-template.tld" prefix="ft" %>

<!-- Defines a page attribute with the value of the property "nome" of the shown object -->
<ft:define id="personName" property="nome"/>

<table style="border-bottom: 1px solid gray">
    <thead>
        <tr>
            <th colspan="5" style="border-bottom: 1px solid gray">(1) <%= personName %></td>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>(2) <ft:label property="gender"/></td>
            <td>(3) <ft:view property="gender"/></td>
            
            <!-- separator -->
            <td width="100px" rowspan="2"></td>
            
            <td>(4) <ft:label property="nascimento"/></td>
            <td>(5)
                <ft:view property="nascimento">
                    <ft:layout>
                        <ft:property name="format" value="dd MMMM yyyy"/>
                    </ft:layout>
                </ft:view>
            </td>
        </tr>
        <tr>
            <td>(6) <ft:label property="username"/></td>
            <td>(7) <ft:view property="username"/></td>

            <td>(8) <ft:label property="pais.nationality"/></td>
            <td>(9) <ft:view property="pais.nationality"/> </td>
        </tr>
    </tbody>
</table>

The two entirely new tags are define and label. The first tag is very similar to the bean:define tag. It defines a new attribute in the page with the value of the given property from the presented object. The label tag allows you to search for the label defined in the renderers' resources using the renderer's conventions for labels.

Top Next: The second situation: give me input