Introduction
In order to improve the layout customization of existing applications, the concept of "templating" was introduced. This document explains all you need to know in order to use the new "templating" system.
In order to create a component (the old way) you need two classes:
_
w.startElement( DIV ); w.startElement( UL ); Iterator<UIComponent> it = component.getChildren().iterator(); while (it.hasNext()){ Menu m = (Menu) it.next(); w.startElement (LI); w.write (m.getText()); w.endElement(LI); } w.endElement(UL); w.endElement(DIV);
Apart from being cumbersome this meant that you had absolutely no chance of customizing the look and feel of a given component (apart from creating a new renderer for the said component). With templating the following scenario is possible when building a component:
<div> <ul> <#list this.children as menu> <li>${menu.text}</li> </#list> </ul> </div>
The template now declares how the component is rendered and can be switched at runtime by defining a new template for same component. The template has access to "variables" which will be replaced by their values at runtime. Modern components require some help of javascript and css, as such it's possible to include that type of content in your templates. You can find more information in the Javascript Section.
_
The template system used in XEO builds upon the Freemaker Library which has its own template language. The manual for creating Freemarker templates can be found here. If you're not familiar with template systems we recommend checking the wikipedia page for a more general approach and the Freemarker page for a more specific one. The main ideia behind templating engines is rendering content using a variable-replacing system, meaning that if you want to render a "Hello" message followed by a username, you would create a template like the following:
Hello ${username}
And pass the variable "username" to the template engine to get something like "Hello John" or "Hello Bill". There are a lot more things you can do inside a template (like iterating through a list, using if/else statements and a lot more (check the Freemarker manual for a full list)).
_
_
The following template is a sample of the ActionButton template component. Notice the use of the <#if> directive to choose whether or not the component should render the HTML. Notice the use of the clientId, id, width and label properties to generate the HTML div and button with the correct attributes.
<#if !this.renderedOnClient> <div id='${this.clientId}'> <button id='${this.id}_btn' class='xwc-actionbutton' style='min-width;${this.width}px'> ${this.label} </button> </div> </#if>
_
In the following example (extracted from the Tabs component) you can see the use of lists and a special directive( @xvw_facet - explained later in this document)
<#list this.children as tab> <div id='${tab.id}' class='xwc-tab-content'> </div> </#list> <@xvw_facet />
You can also use/include Javascript and generate Javascript dinamically (this example generates a JQuery call to create a panel).
<@xvw_script position='footer'> $(function() { $( '#${this.id}' ) <#if this.collapsible> .collapsiblePanel(true); <#else> .collapsiblePanel(false); </#if> }); </@xvw_script>
_
_
At the heart of the template engine there are the variables. When using a template combined with a component you have access to all the properties available in the component. Each component is exported (into its own template) as the this variable and the properties of the component can be accessed like the following:
${this.propertyName}
For the Section component which has properties like visible and title you can do:
${this.visible} or ${this.title}
You also have access to any method the component has that follows Java Beans convention. If the component has getCustomValue() method, you can use ${this.customValue} (with the getCustomValue method not being related with any instance of a XUIBaseProperty).
When you're creating a new template for an existing component refer to the component's API to check which variables are available.
_
_
You may need to create a view and "use" a specific template inside that view, but not necessarily associated with a specific component. In order to support that situation, the Template component was created. Template has no properties other than the standard ones provided by XUIComponentBase, but it has an interesting feature: Any property that is declared in the XML definition, will be passed to the template as ${this.properties.PROPERTY_NAME} . The properties can be literals or expressions, meaning you could have the following in the viewer:
<xvw:template template='myCustomTemplate.ftl' customProperty='World' fruit='#{viewBean.myValue}'/>
And a template like the following:
<div> Hello ${this.properties.customProperty} my favorite fruit is ${this.properties.fruit}! </div>
And a bean with:
public String getMyValue(){ return "Orange"; }
Which would render the following:
<div> Hello World my favorite fruit is Orange! </div>
If you need to pass an XEO Object / List to a template, you need two things:
Declare a property in the template that retrieved the object from the Bean
<xvw:teamplate template='test.ftl' someName='#{beanId.propName}'>
In the bean do the following:
import netgest.bo.xwc.components.template.wrappers.*; public XeoWrapper getPropName(){ boObject obj = load(); OR boObjectList list = loadList(); return WrapperFactory.wrapObject(obj); or return WrapperFactory.wrapList(list); }
_
When you create a template for a component and reference the template by its name using the template property (now available in every component), XEO will search for the template in the following places:
<xvw:actionButton template='vanilla/myTemplate.ftl' />
The template loader will search for the template in "webapps/default/templates/vanilla/myTemplate.ftl" and, if it does not find the template, searches in "src/vanilla/myTemplate.ftl", if the template is not found in any of the locations, an error is thrown.
_
The XUIComponentBase class (the class from which all components derive) was added two new properties (both binding properties whose value can be retrieved from the bean):
The templateContent property allows you to dinamically generate the template's content in the bean and pass it to the component. You need to generate a template compliant with the Freemarker engine and return it as a String to the component: The following example depicts this situation:
(In the Viewer)
<xvw:actionButton templateContent='#{viewBean.content}' />
(In the Bean)
public String getContent(){ return "Hello ${username}" }
_
_
There are special intructions that are available for use in a template. There instructions are:
_
_
If you need to include a Javascript file for use in a template, or add an inline call to a Javascript function, you need the <@xvw_script> directive. It allows you to include files and inline calls at the header or footer of the page. The XVW_Script directive has the following properties:
<@xvw_script position='header' id='jquery' src='javascript/jquery/jquery.min.1.8.2.js' />
Example of how to add a call to a javascript function in a template
<@xvw_script position='footer'> alert(${this.id}); </@xvw_script>
_
If you need to include a CSS file for use in a template, or add a CSS style inline you need the _<@xvw_css> directive. It allows you to include files and inline styles at the header or footer of the page. The XVW_CSS directive has the following properties:
<@xvw_css position='header' src='path/to/myStyles.css' />
Example of how to add inline styles in a template
<@xvw_css position='header'> .myStyle{ color:red; font-size:13px; } </@xvw_css>
_
If there's the need to add a special header to the page (like adding a meta tag to a page, or something like that) you can use the <@xvw_header> directive
Example of adding a meta tag
<@xvw_header> <meta name="description" content="Awesome Description Here"> </@xvw_header>
Or adding Google Chrome Frame
<@xvw_header> <meta http-equiv="X-UA-Compatible" content="chrome=1"> </@xvw_header>
_
The <@xvw_facet> directive is related to the processing of child elements in templates. Several components are "childless", meaning they don't have children to be rendered. While other components can have children and may require that their children be rendered in a special place inside their own template. The <@xvw_facet> directive allows you to specify exactly where children are rendered. Lets see an example with the Section component's template:
<fieldset id='${this.id}' class='ui-widget ui-widget-content xwc-section'> <legend style='ui-widget-header ui-corner-all xwc-legend'> ${this.label} </legend> <@xvw_facet /> </fieldset>
The section component is basically a fieldset wrapping the content of its children. As such, the template draws the fieldset and legend tags, and then instructs the template (through the <@xvw_facet> directive) to render its children inside the fieldset. If a section component has as children a set of rows, they'll be rendered inside the fieldset.
But what if you know you have only a specific set of children and you want to render them individually in certain places? Well The <@xvw_facet> directive is able to help you again. If you need a component (let's assume the generic "template" component) with two children and in the template of that component you need to render those children in specific places then you need to declare each children wrapped in a f:facet component and give the facet a name. That name will be used in the template (with the <@xvw_facet> directive), see the following viewer example:
<xvw:template template='myTemplate.ftl'> <f:facet name='button'> <xvw:actionButton> </f:facet> <f:facet name='grid'> <xvw:gridPanel > </f:facet> </xvw:template>
And a template like the following (see the use of the <@xvw_facet> directive with the name property to render the specific child in that position) :
<div id=${this.id}> <fieldset> <@xvw_facet name='button' /> </fieldset> <ul> <li>TESTE </li> </ul> <@xvw_facet name='grid' /> </div>
Each child must have a unique id when wrapped inside a f:facet component.
In order for you to invoke an action of the server side. This typically requires you to declare a xvw:menu component in the viewer that registers the action available in the bean, and after that in th template you need to invoke something like ${xvw.js.ajaxCommand('formId','commandId')} which can be a dificult task if that template is container in a viewer which is container in another viewer. The formId and commandId in these situations may be unknown. To help with that, we created a command create mechanism right in the template. This is best show with an example:
<@XUICommand id="login" serverAction="!{viewBean.login}"/>
<button name="${login.clientId}" onclick="${xvw.js.ajaxCommand(login)}" />
Using the <@XUICommand> directive (with the approriate, required, id and serverAction properties set) you can dynamically create a command that can be passed to the xvw.js.ajaxCommand function (which in turn will generate the javascript invocation and finding the approriate id's for form and command). What are the restrictions? Well, when you set the id in the <@XUICommand> directive that id will be available in template's context as a variable (much like the xvw and this variables). The biggest restriction at the moment is when declaring the serverAction, you need to declare it like !{beanId.method} much like what you use in a viewer with #{beanId.method}. The main issue is that since the # character is special to the template language.
This makes the creation of commands to invoke server-side actions very simple.
In the same way, you can create dynamic input fields to help with from creation. See the following example:
<@XUIInput name='username' valueExpression="!{viewBean.username}" />
<input name='${username.clientId}' value='${username.value}' />
The <@XUIInput> directive is used to create an input command and assign it a name (which makes it available in the context as a variable) and a binding expression. The binding expression has the same issue as the XUICommand directive (must start with a '!' instead of a '#'). The value submited by the use will be placed in the "username" variable in the bean using the necessary setUsername method that must exist)
_
_
If for some reason you need to process a template within your code you use can the class netgest.bo.xwc.components.template.util.CustomTemplateRenderer, which has two methods:
public static String processTemplateFile(String templateName, Map<String,Object> context); public static String processTemplate(String templateContent, Map<String,Object> context);
The processTemplateFile method receives the name of a template (same rules defined earlier apply to finding templates) and a Map with the mappings between variables and their values to be applied in the template.
The processTemplate method receives the content of the template as a String and a Map with the variables, for instance you could have the following code in your application:
public String processCustomActionAndGenerateHtml(){ // Business logic String template = "Hello ${user} "; Map<String,Object> map = new HashMap<String,Object>(); map.put( "user" , "John" ); String result = CustomTemplateRenderer.renderTemplate( template , map ); return result; }
And the result would be:
Hello John
_
Several components were created to integrate individual objects, object lists and lovs with templates. The details for each component is in the following sections:
If you're familiar with XEO's Java API you know that to get the value of an instance attribute you need to do instance.getAttribute("name").getValueString() and to get a bridge/collection instance.getBridge("bridgename").
Inside the templates we've made things simpler and if you you're rendering an object (using xeoobject component) using a template you could do:
example:
<ul> <#list this.list as item> <li><a href="none.html">${item.username.value}</a></li> </#list> </ul>
XEO it's an OOP platform, so you could have complex structures described in you're objects. Using the templates you could easily navigate in those structures and render all the information that relates to them. For instance if you have and object person that has a relation to an object address and that address has an attribute called city, in order to render the city value you can do:
${this.xeoobject.address.value.city.value} or ${this.xeoobject.address$.city$} (if you end an attribute name with the '$' char
to the template processor it's the same as getting it's value)
In the previous example if the same person object as a bridge/collection called friends, and this friends are also persons. To render the city of each person friends you have to write a template like this:
<ul> <#list this.list as person> <#list person.friends as friend> <li><a href="none.html">${friend.address$.city$}</a></li> <#/list> </#list> </ul>
Xeolist (xeo:xeolist)
The Xeolist component allows you to display a list of objects using a template. The component has the following properties:
<xeo:xeolist name='UserNameList' boql='select Ebo_Perf' template='list.ftl' />
And the template list.ftl:
<ul> <#list this.list as item> <li><a href="none.html">${item.username}</a></li> </#list> </ul>
This will generate a list with a link for the first 30 username's in the application
If you want to do Pagination in the previous example you could change the template do be:
<ul> <#list this.list as item> <li><a href="none.html">${item.username}</a></li> </#list> </ul> <a href="${this.first}">First</a> <a href="${this.previous}">Previous</a> <a href="${this.next}">Next</a> <a href="${this.last}">Last</a> Page ${this.page} of ${this.pages}
In the previous example the pagination uses the default GET navigation type. If you want to use ajax as the navigation type, you're xeo:list component needs to be inside a form (ex: xvw:form) and in the template you need to change the <a href... to button's or use javascript: in the href property. Please note that you've have to do your own validations inside the template if you want to hide the "next" option when you're in the last page or the "previous" option when you're in the first page. This can be done using the variables ${this.page} and ${this.pages} and freemarker conditional operators.
<ul> <#list this.list as item> <li><a href="none.html">${item.username}</a></li> </#list> </ul> <#if (this.page?number>1)> <a href="${this.first}">First</a> </#if> <#if (this.page?number<this.pages)> <a href="${this.previous}">Previous</a> </#if> <a href="${this.next}">Next</a> <a href="${this.last}">Last</a> Page ${this.page} of ${this.pages}
Inside the template of a xeolist the properties and functions you can use are:
The Xeoobject component allows you to associate a single instance with a template. It has the following properties
<xeo:xeoobject boui="56677" name="var" template='site/menus.ftl' />
And use a template like:
<div> <span>${this.xeoobject.name.value}</span> <span>${this.xeoobject.age$}</span> <ul> <#list this.xeoobject.bridge.value as menu> <li>${menu.name}</li> </#list> </ul> </div>
The Sqllist component allows you to display the rows/fields of an SQL query using a template, it works similar to xeolist but instead of displaying XEOObjects it's used to display database records . It has the following properties:
<xeo:sqllist sql="select name,age from person" name="ListPerson" template='site/sqllist.ftl' />
And use a template like:
<div> <ul> <#list this.list as item> <li>${item.name.value}</li> <li>${item.age.value}</li> </#list> </ul> </div>
In a Sqllist inside a template you've access to the same properties used in a Xeolist. In the fields you only have access to field.value and field.label (if the database supports it), the other properties (visible, enabled, etc...) are only available to XEOObjects.
The Genericlist component allows you to display a list of any type of information. You have to use a GenericDataListConnector and wrapp your data inside of it, or you could implement a new DataListConnector. Like the Sqllist it works similar to xeolist but instead of displaying XEOObjects it's used to display information from any source wrapped in a record/field manner . It has the following properties:
<xeo:genericlist dataSource="#{viewBean.connector}" name="ListGeneric" template='site/genericlist.ftl' />Bean:
public GenericDataListConnector getConnector() {
ArrayList<Person> arr= new ArrayList<Person>(); arr.add(new Person("Antonio","Cruz","34")); arr.add(new Person("Antonio2","Cruz2","35")); arr.add(new Person("Antonio3","Cruz3","36")); connector.createColumn("name","Name"); connector.createColumn("lastname","Last Name"); connector.createColumn("age","Age"); Iterator<Person> it = arr.iterator(); while (it.hasNext()) { Person currPerson=it.next(); connector.createRow(); connector.createRowAttribute("name", currPerson.getName()); connector.createRowAttribute("lastname", currPerson.getLastname()); connector.createRowAttribute("age", currPerson.getAge()); } return connector; }
And use a template like:
<div> <ul> <#list this.list as item> <li>${item.name.label} - ${item.name.value}</li> <li>${item.age.label} - ${item.age.value}</li> <li>${item.lastname.label} - ${item.lastname.value}</li> </#list> </ul>
</div>
The Arraylist component allows you to display a list of java Objects wrapped inside an ArrayList. It doesn't use Connectors , so the rules for rendering fields and pagination do not apply to this component. It has the following properties:
<xeo:arraylist dataSource="#{viewBean.persons}" template='site/arraylist.ftl' />Bean (Person is a object with the properties Name, Age and Lastname):
public ArrayList<Object> getPersons() { ArrayList<Person> persons= new ArrayList<Person>(); persons.add(new Person("Antonio","Cruz","34")); persons.add(new Person("Antonio2","Cruz2","35")); persons.add(new Person("Antonio3","Cruz3","36")); return persons; } And use a template like:
<div> <ul> <#list this.list as item> <li>${item.name}</li> <li>${item.age}</li> <li>${item.lastname}</li> </#list> </ul>
</div>
Errors in template processing should be printed directly in the output stream as well as in the console (with a stack trace) for easy problem detection (it may not render very nicelly in all situations but you'll get to notice the problem)
_
_
There a number of context variables which can be used in a template, namely:
${xvw.bundles.MESSAGE}
And it will render the MESSAGE inside the template. Notice the "xvw.bundles". Everything provided by the framework is inside the xvw variable/namespace. As you'll see in the following examples.
If you are creating a component that needs to make Ajax calls and don't want to hardcode the reference to the Javacript function you can use the provided variable name XVWScripts and call the getAjaxCommand(Component) method, like in the following example (extracted from the template of the ActionButton component):
$( '#${this.id}_btn' ).button().click( function () { ${xvw.js.ajaxCommand(this)}; return false; })
In this situation we're generating the Ajax call for the "this" component. The following functions are available
${xvw.js.ajaxCommand(component,'x','LITERAL')} will invoke the command with value 'x', while:
var x = 99;
${xvw.js.ajaxCommand(component,'x','VAR')} will invoke the command with value '99'
Here you can find some template examples:
_
<@xvw_script src='jquery-xeo/xwc-panel.js' />
_
_
<@xvw_script position='header' id='xvw-flattree' > XVW.showHideMenu = function (elemParent){ var elem = $(elemParent).next(); elem.toggle(); if (elem.hasClass('xwc-tree-panel-highlighted')) elem.removeClass('xwc-tree-panel-highlighted'); else elem.addClass('xwc-tree-panel-highlighted'); } </@xvw_script>
_
_
<#if !this.renderedOnClient> <div id="${this.clientId}"> <div id="${this.id}" class='xwc-panel' title='${this.title!}'> <@xvw_facet /> </div> </div> </#if>
_
_
<@xvw_script position='footer'> $(function() { $( '#${this.id}' ) <#if this.collapsible> .collapsiblePanel(true); <#else> .collapsiblePanel(false); </#if> }); </@xvw_script>
_
<select style='width:100%' id='${this.id}' name='${this.clientId}'> <#list this.lovMap?keys as item> <#-- Iterate through the keys of the map --> <#if (this.lovMap[item] == this.value!)> <#-- Get the value from the map --> <option value='${item}' selected='selected'>${this.lovMap[item]}</option> <#else> <option value='${item}'>${this.lovMap[item]}</option> </#if> </#list> </select>
_
_
<span style='float:left; margin:0 7px 20px 0' <#switch this.messageBoxType> <#case "ERROR"> class='xwc-messagebox-icon ui-icon ui-icon-alert'> <#break> <#case "INFO"> class='xwc-messagebox-icon ui-icon ui-icon-info'> <#break> <#case "QUESTIOn"> class='xwc-messagebox-icon ui-icon ui-icon-help'> <#break> <#case "WARNING"> class='xwc-messagebox-icon ui-icon ui-icon-notice'> <#break> </#switch> </span>
_
_
<#if menu.actionExpression??> <#-- If menu.getActionExpression has a value then execute what's inside the if -->
_
_
<@xvw_script position='footer'> $(function() { $( '#${this.id}' ) .dialog( { 'modal' : ${this.modal?string} ,'width' : ${this.width} ,'height' : ${this.height} ,'title' : '${this.title}' }); }); </@xvw_script>
_
_
<#-- This is a comment -->
_
There are three new types of annotations that you can use in a XEO Bean. They are
XUIWebCommand
A XUIWebCommand is an annotation you can put in a given method of a bean, like the following:
@XUIWebCommand(name='name',value='test') public void exampleMethod1(){}
This means that when a viewer (that is associated with this bean) is request with a parameter 'name' and value 'test' it will execute the exampleMethod1. Meaning if the browser requests the following viewers/Viewer?name=test the exampleMethod1 method will be invoked
XUIWebDefaultCommand
Like the XUIWebCommand but it's a command that will be executed by default when you open a viewer (unless another command was executed)
XUIWebParameter
Allows you to set the value of a parameter directly in the bean, for example:
@XUIWebParameter(name = 'paramInUrl', defaultValue='empty' ) public void setSomeParam(String name){ //Do whatever you want with the value }
if you call this viewer having this bean with a parameter ?paramInUrl=someValue the String "someValue" will be passed to the setSomeParam method as an argument, if the parameter does not exist in the url, the default value will be passed.
In order to control what scripts and CSS are included in each viewer we're now providing the possibility of registering a Theme for each renderkit. By default XEO uses an instance of the netgest.bo.xwc.frarmework.XUITheme interface. Now you can register your own implementation to include whatever scripts and css files in each viewer. In order to do that you need the following:
In the boconfig.xml file, register the renderKit and themeClass you want to use (there are 4 renderKits available in XEO - XEOHTML, XEOJQUERY, XEOXML and XEOV2(@Deprecated)) like the following:
(by default the XEOHTML rendeKit is used)
<renderKits default='XEOJQUERY/XEOHTML'> <renderKit id='XEOJQUERY' themeClass='path.to.class.implementing.XUITheme' /> </renderKits>
XUITheme has the following interface
public String getResourceBaseUri(); public void addStyle( XUIStyleContext styleContext ); public void addScripts( XUIScriptContext styleContext ); public String getBodyStyle(); public String getHtmlStyle();
The most important methods are the addStyle and addScripts which allow you to decide which scripts will be included (you can check the existing ExtJsTheme and JQueryTheme for reference of included files)
We've introduced a new feature that allows you to use the viewers much like facelets and templates (http://www.mkyong.com/jsf2/jsf-2-templating-with-facelets-example/). You can create a "Template Viewer" where you define certain areas that can be replaced, like the following example:
<xvw:root xmlns:xeo="http://www.netgest.net/xeo/xeo" xmlns:xvw="http://www.netgest.net/xeo/xvw"> <xvw:viewer beanClass="pt.itds.ams.beans.ToolBarBean" beanId="viewBean"> <xvw:form> <table> <tr> <td> <xvw:insert src='header.xvw' name='header'/> </td> </tr> <tr> <td> <xvw:insert src='default.xvw' name='content'></xvw:insert> </td> </tr> <tr> <td> <xvw:insert src='footer.xvw' name='footer'></xvw:insert> </td> </tr> </table> </xvw:form> </xvw:viewer> </xvw:root>
Everything is like it was before, but notice the existance of the xvw:insert components. These components allow you to define "substitutable areas" when using this viewer as a template. These areas can be substituted by other content while providing a default one when a substitution does not occurs. In order to use these components you have to set two attributes:
<xvw:root xmlns:xeo="http://www.netgest.net/xeo/xeo" xmlns:xvw="http://www.netgest.net/xeo/xvw">
<xvw:viewer>
<xvw:container>
<xvw:outputHtml valueExpression='HEADER'></xvw:outputHtml>
</xvw:container>
</xvw:viewer>
</xvw:root>
In order for a page to use the template you need to create the viewer with the following declaration:
<?xml version="1.0" encoding="UTF-8"?>
<xvw:root xmlns:xeo="http://www.netgest.net/xeo/xeo" xmlns:xvw="http://www.netgest.net/xeo/xvw">
<xvw:viewer beanClass="path.to.some.Bean" beanId="viewBean">
<xvw:composition template='Template.xvw'>
<xvw:define name='content' >
<!-- Content to include, can be html or components -->
</xvw:define>
<xvw:define name='footer' >
<!-- Content to include, can be html or components -->
</xvw:define>
</xvw:composition>
</xvw:viewer>
</xvw:root>
The first child of the xvw:viewer component must be a xvw:composition with an attribute template pointing to the template file (the one with the xvw:insert components). The children of the xvw:composition component must be xvw:define components. Each xvw:define component must have a name attribute whose value must match the name found in one of the xvw:insert components in the template file. When you do this, the content of each of those xvw:insert components that have a matching name attribute, will be replaced with the xvw:define component's children.
No permission to view TWiki.WebTopBar