UIX Developer's Guide
Go to Table of Contents
Contents
Go to previous page
Previous
Go to next page
Next

14. UIX Includes and Templating

By now, you should be getting used to creating UIX pages. But you may be pretty tired of writing the same bits of UIX over and over again in all of your pages. This is not only tedious, but also a maintenance nightmare - to add another global button or change your branding images, you might need to update every last UIX file in your application.

There's got to be a better way - and there is. UIX offers two features that help you divide your pages into reusable pieces - "includes" and templating. Your user interfaces will be more maintainable, more customizable, and will even use less memory.

This chapter contains the following sections:

The Problem

You've probably written a few pages that look like this:

 <pageLayout>
   
   <globalButtons>
     <globalButtonBar>
        ...
     </globalButtonBar>
   </globalButtons>
    
   <tabs>
     <tabBar>
         ...
     </tabBar>
   </tabs>

   <productBranding>
     <image .../>
   </productBranding>
   
   <!-- etc... -->
 </pageLayout>

There's a good chance that all of your pages look like this. Each has a <pageLayout>, with the same <globalButtons>, the same <tabs>, the same branding. It's easy enough to cut-and-paste your way through this, but what about when you need to change content across all of your pages - like adding another global button? You'll have to modify every UIX file. That's tedious and error prone. It also wastes memory and time - each page has to load and reparse the same set of content, and the cached result trees aren't shared at all. Wouldn't it be better to define these pieces of UIX once, then reuse them?

A Solution: Includes

UIX's <include> element offers you a way out of this problem. Instead of repeating the same content over and over again, you can write the content once, then include it from each page that needs it. For example, here's what your page might look like:

File somePage.uix

 <pageLayout xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
   
   <globalButtons>
     <include ctrl:node="gbInclude"/>
   </globalButtons>
    
   <tabs>
     <include ctrl:node="tabsInclude"/>
   </tabs>

   <productBranding>
     <include ctrl:node="pbInclude"/>
   </productBranding>
   
   <!-- etc... -->
 </pageLayout>

And here's one of the included pieces:

File gbInclude.uix

<globalButtonBar xmlns="http://xmlns.oracle.com/uix/ui">
 <contents>
   <globalButton .../>
   <globalButton .../>
 </contents>
</globalButtonBar>

Let's look at one of those<include> elements:

     <include ctrl:node="gbInclude"/>

UIX <include> elements only have one attribute: node. Here, we've put that attribute in the Controller namespace. This tells UIX to interpret the target not as a file name, but as a UIX Controller page name. That's why there's no .uix after gbInclude. We'll see other syntaxes for using the node attribute a little later, but if you're using uiXML, this is the preferred form.

The target UIX files can look just like any other Controller-based uiXML file. They start with:

 <?xml version="1.0" encoding="UTF-8"?>
 <!-- an included file -->
 <page xmlns="http://xmlns.oracle.com/uix/controller">
     ... etc.

...and can include any data binding or content. (As of UIX 2.0.5, included files can include event handling, but these handlers are ignored. We hope to address this limitation in the near future.) However, you're also allowed to omit the <page> and <content> elements and go straight to the content:

 <?xml version="1.0" encoding="UTF-8"?>
 <!-- an included file, but just content -->
 <pageLayout xmlns="http://xmlns.oracle.com/uix/ui">
     ... etc.

Relative Versus Full Page Names

UIX Controller page names can be specified in UIX both as relative names or full page names. If the name starts with a forward slash ("/"), it's treated as a full name (after removing the slash). So, for example, if you placed all of your include files inside a "/common" subdirectory off of the root of your UIX directory, you could include them with:

     <include ctrl:node="/common/includedName"/>
If there isn't a slash, UIX interprets the path as relative to the including file. For example, if you're in page "foo/somePage", then these two includes:
     <include ctrl:node="anotherPage"/>
     <include ctrl:node="bar/thirdPage"/>
are treated as includes of "foo/anotherPage" and "foo/bar/thirdPage".

It's worth remembering that including with "ctrl:node" is including a UIX Controller page, not a uiXML file. In particular, this means that uiXML-based or UIX Components-based pages explicitly registered with the UIX Controller can be included even if no UIX file is accessible.

Includes Without UIX Controller

If you're not using the UIX Controller, you can still use <include>. If you omit the Controller namespace from the "node" attribute, UIX interprets the value as a file name:

 <pageLayout>
   
   <globalButtons>
     <include node="gbInclude.uix"/>
   </globalButtons>
    ...etc...
 </pageLayout>

If you write your includes this way, the target included file must not start with a <page> element; it must begin directly with the UI content. Because of this, this type of include cannot include any event handlers (though, as noted above, The UIX Controller doesn't yet support event handlers in included files anyway).

In addition, you can also databind the node attribute:

 <pageLayout xmlns:data="http://xmlns.oracle.com/uix/ui">
   
   <globalButtons>
     <include data:node="gbInclude@demo:someSource"/>
   </globalButtons>
    ...etc...
 </pageLayout>

In this example, the node is retrieved at render-time from the someSource DataObject. For more information on this technique, see Dynamic Structure for UIX Pages.

Limitations of "includes"

The most fundamental problem with <include> is that you're pretty much committed to taking the include file "as is". From the parent page, you can't set attributes on elements inside the included file, or add any elements. This is pretty limiting. There is a way around this: DataObjects seen by the parent page are still visible inside the included page. So, you can code:

 <dataScope>
   <provider>
     <data name="demo:someData"> ...</data>
   </provider>
   <contents>
     <include .../>
   </contents>
 </dataScope>

... and then reference "demo:someData" inside the included file. And if you put UINodes inside the DataObject, you could even use <include data:node="..."/> to get child elements inside. But this is a bit messy, and very inelegant.

You might have tried writing the includes this way:

 <pageLayout xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
   
   <include ctrl:node="pageIncludes"/>
    ...
 </pageLayout>

and then change the included file to look like:

<globalButtons>
 <globalButtonBar>
    ...
 </globalButtonBar>
</globalButtons>

<tabs>
 <tabBar>
  ...
 </tabBar>
</tabs>

...etc...

That'd be convenient, but it doesn't work. The included file has to be a valid XML file, and that means that it has to have only one root element! Or, perhaps you might have tried writing

 <pageLayout xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
   
   <include ctrl:node="gbInclude"/>
    ...
 </pageLayout>

with an included file that looked like:

<globalButtons>
 <globalButtonBar>
    ...
 </globalButtonBar>
</globalButtons>

This way, you wouldn't have to type <globalButtons> in every including file. But, unfortunately, this also doesn't work. The <include> element can only include UINode UIX elements - elements that map directly to a UIX Components UINode. <globalButtons> isn't such an element - it's a wrapper element used by <pageLayout>, and it can't stand on its own.

These limitations are especially grating for <pageLayout>. UIX templates solve all of these problems!

Another Solution: Templates

Templating takes an entirely different tack to solving the problem of building modular UIX files. Templating lets you define new UINode elements in terms of pre-existing elements. These new elements can have all the attributes and child elements of any built-in node. Instead of forcing you to extract many separate bits of content from the bottom of the node tree, you can start from the top of the tree:

While using templates is easy - it's just like using any other UIX element - building template definitions is a bit harder. Template definitions rely on data binding, and it helps to have a basic knowledge of the UIX Components APIs - if you don't know the difference between "named children" and "indexed children", this might be a bit difficult. But templating is much easier than writing the Java Renderers and NodeParser classes that you originally needed to write!

In the subsequent examples, we'll build a simple template which we will define in a file called demoPageLayout.uit, which will:

First, we'll use this template, then show how to write the template definition.

Using a Pre-written Template

Using a template in your UIX file is straightforward. First, you must add tags to your UIX file to load the template. Second, you refer to the template in your content.

Templates are loaded by a new section of the UIX Controller <page> element: <templates>. Unlike most sub-elements of <page>, this element must be placed in the UIX Components namespace:

 <page xmlns="http://xmlns.oracle.com/uix/controller">
    <templates xmlns="http://xmlns.oracle.com/uix/ui">
       ...
    </templates>
 </page>

This element must appear before <head> or <content> - that is, you have to define the templates before you use them. A <templates> element contains a series of <templateImport> elements, each of which locates a template definition file:

 <page xmlns="http://xmlns.oracle.com/uix/controller">
    <templates xmlns="http://xmlns.oracle.com/uix/ui">
      <templateImport source="demoPageLayout.uit"/>
      <templateImport source="demoSomethingElse.uit"/>
    </templates>
 </page>

We've used the ".uit" suffix as a convention to distinguish these files from normal .uix files - template definition files cannot be displayed directly. You're also allowed to use <templateDefinition> elements here to define a template inline - but we'll leave that element for later. (There is not yet a UIX Components-only syntax for loading templates. It is not clear yet if one will be needed. We will, however, be providing a tool that builds a Java bean class based on a template file.)

Each template definition file defines one element; like other UIX elements, they must belong to a specific namespace. The elements defined by templates cannot share namespaces with other UIX elements; for this example, let's use a demo namespace:

   xmlns:demoTmps="http://www.example.org/demo/templates"

We'll now use our new "demoPageLayout" element in this namespace (assuming, of course, that it was properly defined in "demoPageLayout.uit"):

 <page xmlns="http://xmlns.oracle.com/uix/controller"
           xmlns:demoTmps="http://www.example.org/demo/templates">
    <templates xmlns="http://xmlns.oracle.com/uix/ui">
      <templateImport source="demoPageLayout.uit"/>
    </templates>

    <content>
      <demoTmps:demoPageLayout xmlns="http://xmlns.oracle.com/uix/ui"
                                   selectedTab="3">
        <start>
          <sideNav></sideNav>
        </start>

        <demoTmps:topHead>
          <header text="A first header">

          </header>
        </demoTmps:topHead>

        <contents>
          <header text="A second header">

          </header>
        </contents>
      </demoTmps:demoPageLayout>
    </content>
 </page>

Let's walk through this example; there's some trickiness here with namespaces:

  1. First, we've defined our template namespace up top; this has to match up with the namespace that gets used in your template definition.
  2. Second, we import the template using a <templateImport> element. Note that it's inside of the new <templates> element.
  3. Then, in the content, we use our <demoPageLayout> element. Note that this element is in the demoTmps namespace, but we've switched the default namespace to UIX Components.
  4. We've also defined a selectedTab attribute, which will designate which tab (in the template) is selected.
  5. Next up, a <start> element. When we write the definition of <demoPageLayout>, we'll describe it as a <pageLayout> element plus extra features. So it supports all the attributes and elements of <pageLayout>. Since this element comes from <pageLayout>, which is in the UIX Components namespace, <start> must also be in the UIX Components namespace.
  6. Now, the<demoTmps:topHead> element. This is a new element we're defining to be the first element of <pageLayout>'s contents. Since it's an addition to <pageLayout>, it goes in the demoTmps namespace.
  7. And, now the <contents> element - no change here over how this element is used.

That's it - other than being in a new namespace, this element looks a lot like any other UIX element. Next, we'll discuss how you'd define that template.

Writing a Template Definition

First, let's write out the full template definition file (demoPageLayout.uit). Then, we'll walk through it, section by section.

Example:

<?xml version="1.0" encoding="UTF-8"?>
<templateDefinition xmlns="http://xmlns.oracle.com/uix/ui"
          xmlns:ui="http://xmlns.oracle.com/uix/ui"
          xmlns:data="http://xmlns.oracle.com/uix/ui"
          targetNamespace="http://www.example.org/demo/templates"
          localName="demoPageLayout">

 <!-- define the template's type information -->
 <type base="ui:pageLayout">
   <namedChild name="topHead"/>
   <attribute name="selectedTab" javaType="int"/>
 </type>

 <!-- define the content of the page -->
 <content>
   <pageLayout>
     <!-- magic incantation to be explained below -->
     <attributeMap><rootAttributeMap/></attributeMap>

     <!-- another magic incantation -->
     <childMap><rootChildMap/></childMap>

     <!-- set up a default set of tabs -->
     <tabs>
       <tabBar data:selectedIndex="selectedTab@ui:rootAttr">
         <contents>
           <link text="Tab 1"/>
           <link text="Tab 2"/>
           <link text="Tab 3"/>
           <link text="Tab 4"/>
           <link text="Tab 5"/>
         </contents>
       </tabBar>
     </tabs> 


     <!-- set up a default set of global buttons -->
     <globalButtons>
       <globalButtonBar><!-- ... --></globalButtonBar>
     </globalButtons>
 
     <!-- now, the content of the pageLayout -->
     <contents>
       <!-- Start with the "topHead" child at the top -->
       <rootChild name="topHead"/>
       <!-- Then add the indexed children -->
       <rootChild name="contents"/>
     </contents>
   </pageLayout>
 </content>
</templateDefinition>

The <templateDefinition> Element

Here's the <templateDefinition> element we just defined:

<templateDefinition xmlns="http://xmlns.oracle.com/uix/ui"
          xmlns:ui="http://xmlns.oracle.com/uix/ui"
          xmlns:data="http://xmlns.oracle.com/uix/ui"
          targetNamespace="http://www.example.org/demo/templates"
          localName="demoPageLayout">

Like everything else in templating, the <templateDefinition> element belongs in the UIX Components namespace. We've also set up a couple of other namespaces - one for explicitly referring to UIX Components, and a second for data binding.

There's also two new attributes, used to define the name of the element we're defining: targetNamespace and localName. Together, these attributes in our example say that we're defining a <demoPageLayout> element in the "http://www.example.org/demo/templates" namespace.

The <type> Section

The <type> section is used to define the syntax of the template element; that is, how developers can pass information into your template. Here's the above example:

  
 <!-- define the template's type information -->
 <type base="ui:pageLayout">
   <namedChild name="topHead"/>
   <attribute name="selectedTab" javaType="int"/>
 </type>

Again, let's walk through each line of this example:

  1. <type base="ui:pageLayout">
    <type> elements can have a base attribute. This attribute specifies that type of element you're extending. By specifying ui:pageLayout, we've stated that the <demoPageLayout> element will support all of the features that a normal <pageLayout> element would support - plus whatever we add, but minus what we override.

    If the base attribute is omitted, the template element will support all of the default UIX Components attributes.

  2. <namedChild name="topHead"/>
    Next, we've defined a single named child, named topHead. This means that <demoPageLayout> supports a child element named <topHead>, which will in turn contain a piece of UI content. You'll see below how to plug that bit of content in. A template can use any number of <namedChild> elements, or use none at all.
  3. <attribute name="selectedTab" javaType="int"/>
    Finally, we've defined an attribute named selectedTab. Every attribute that's supported above and beyond those from the "base" element should be listed here.

    The javaType attribute is required - it identifies the type of the attribute. You can use a full Java class name, but we support the following abbreviations:

    UIX includes built-in parsing support for these types, as well as support for the following "complex types" (see below for the meaning of this term):

    Other Java types are allowed, but can only be used in conjunction with data binding.

You might wonder why we've used this custom format for type information instead of one of the standards - in particular, why haven't we used XML Schema? We considered using these standards, but they suffered from three problems. First, they are comparatively very expensive to parse - we needed to load template files quickly. Second, XML Schema is actually too expressive; it can define nearly unlimited number of types of element syntax, but templating only allows a much smaller set of syntax. Third, XML Schema has no built-in support for mapping to Java types. However, none of this prevents us from generating an XML Schema document off of the <type> element in a template definition - and we will be adding exactly this functionality.

Defining the Content

The content of a <template> page is defined inside a <content> element:

 
 <!-- define the content of the page -->
 <content>
   <pageLayout>
       ...
   </pageLayout>
 </content>

The content in here defines what this template will look like. Here we've chosen to start with a <pageLayout> element to render our page layout. That makes sense, since we're claiming to be an extension of a <pageLayout>, but there's absolutely no requirement that the element you extend actually appear in your content at all! The <type> information only talks about how developers can pass information into your template - but your template has complete control over how to use that information.

Now is a good time to bring up a subtle point to help Java coders understand templates. When this template is used in pages, you might think that each of those pages now contains <pageLayout> element - and if you walked through that page using UIX Components, you'd eventually find the page layout UINode. But this isn't at all how templates work. The UINode that gets added to your page is just a <demoPageLayout> - and only that. It doesn't matter how complicated the content of the template is - to the page that uses it, it seems as though only a single UINode has been created. When that node is asked to output itself, it privately uses a complex - but shared - UINode tree. The exact details of how this happens are a little too complex to describe here - and you don't need to understand this code to use templates - but the curious can look at the oracle.cabo.ui.composite package.

One effect of this approach may affect you. You can freely use <dataScope> elements inside your template to define data providers - they will have no side effects on the file that uses the template. This is a feature - there's no need to worry about strange side effects and name conflicts.

Accessing Attributes

Now, let's start to take advantage of some of the content set on (and added inside of) the <demoPageLayout> element. First, let's consider the attributes. Attributes can be retrieved either individually or all together as a group.

First, let's see how to grab all of the attributes:

   <pageLayout>
     <attributeMap><rootAttributeMap/></attributeMap>
      ...

This line, <attributeMap>, etc., says "take all of the attributes defined on <demoPagePageLayout>, and add them to <pageLayout>." So, for example, if a developer writes:

 <demoTmps:demoPageLayout rendered="true"
                              title="Some title"> ...

...then both of those attributes will be implicitly set on the <pageLayout> as well. Anything specified directly on the <pageLayout> inside the template will override the parent attributes. For example, if you always wanted quick links off, you could write:

   <pageLayout quickLinksShown="false">
     <attributeMap><rootAttributeMap/></attributeMap>
      ...

... and quick links will be off, no matter what's set on <demoPageLayout>.

Attributes can also be accessed one at a time. For this feature, templates automatically have a special DataObject available for databinding. This DataObject is named "rootAttr", and is in the UIX Components namespace:

     <tabs>
       <tabBar data:selectedIndex="selectedTab@ui:rootAttr">
         <contents>
            ...
         </contents>
       </tabBar>
     </tabs> 

Here, we've declared that the "selectedIndex" of the <tabBar> should follow the "selectedTab" attribute of <demoPageLayout>.

One more thing: you might be concerned about using <rootAttributeMap> here - after all, that does put the selectedTab attribute onto <pageLayout> along with all the attributes it expects. However, uiXML elements ignore attributes they don't know anything about. So, while you can run into problems when you reuse attribute names, this instance is safe.

Accessing Named Children

Just like attributes, named children can be used all together or one at a time. First, the code for grabbing all the named children:

   <pageLayout>
      ...
     <childMap><rootChildMap/></childMap>
      ...

This line, <childMap>, etc., says "take all of the named children defined on <demoPagePageLayout>, and add them to <pageLayout>." So, for example, if a developer writes:

 <demoTmps:demoPageLayout>
   <pageHeader>
     <globalHeader>...</globalHeader>
   </pageHeader>
 </demoTmps:demoPageLayout>

... then that pageHeader named child will be implicitly copied to the <pageLayout> - along with start, end, contentFooter, and all the other named children supported by <pageLayout>. Again, any children defined explicitly inside the template will act as overrides.

The <demoTmps:topHead> named child isn't supported by page layout - so <rootChildMap> isn't enough to get that child to be rendered. For that, you have to explicitly include it, as we did in our example, by using the <rootChild> element:

 <pageLayout>
   ...
   <contents>
     <rootChild name="topHead"/>
      ...
   </contents>

This <rootChild> element will insert the topHead named child as the first (consequently, top) element inside the <pageLayout>. You can use this element in ways you might not have guessed - for example, it's perfectly fine to reuse the same named child multiple times.

Accessing Indexed Children

Unlike named children and attributes, indexed children (that is, the children inside of <contents>) can only be accessed as a group. You'll still use the <rootChild> element, but with a value for its name attribute: contents:

 <pageLayout>
   <contents>
     <rootChild name="topHead"/>
     <rootChild name="contents"/>
   </contents>
 </pageLayout>

This example puts the indexed children immediately after (below) the topHead named child.

Two important caveats:

  1. <rootChild name="contents"/> can only be used inside <contents>. It may never be placed in any other element.
  2. <rootChild name="contents"/> can only be used once per <contents>. It's perfectly legal to add it more than once in your template, but only one instance can be inside each <contents>.

Including Other Templates

Template definitions can refer to and use other templates. In fact, templates can be refinements of other templates. This is an extremely powerful tool of UIX. For instance, you might set up a whole series of templates:

Now, a single change to the common page layout propagates to every single page in your application! This is a dramatic improvement in both maintainability and customizability - customers can replace pieces of content for an entire application by touching only a single file. And all at very, very little cost to the page developers. Once a template is written, all the developers see is ordinary UIX. Contrast this with JSP templating implementations, where using templates requires radically changing the appearance and design of your JSP code.

How do you refer to other templates? Add a <templates> element to the <templateDefinition> element (this is similar to adding a template to a UIX file):

<templateDefinition ...>
 <templates>
   <templateImport source="subtemplate.uit"/>
 </templates>

 <type> ...</type>
 <content> ...</content>
</templateDefinition>

This <templates> elements must appear before both <type> and <content>, so that these elements can use the included templates. The included templates can be used inside of the <content> element, or used for the base attribute on the <type> element.

Adding Documentation to Your Definition

The whole idea of writing a template is to provide a reusable component for many other developers. But reusable components aren't very reusable if they aren't documented. You might use simple XML comments for documentation, but comments are difficult to parse, are easily lost in transformations, and can't contain their own child elements. Towards this end, <templateDefinition> supports several additional elements only for documentation purposes.

First, each <templateDefinition> file can contain <version>, <author>, and <documentation> elements. Each of these elements can contain any attributes, text or child elements - the content is completely left alone by our runtime (though it must be well-formed XML).

<templateDefinition xmlns="http://xmlns.oracle.com/uix/ui"
          xmlns:ui="http://xmlns.oracle.com/uix/ui"
          xmlns:data="http://xmlns.oracle.com/uix/ui"
          targetNamespace="http://www.example.org/demo/templates"
          localName="demoPageLayout">

 <author>J. Developer</author>

 <version>2.0</version>

 <documentation>
    This element defines a subtype of &lt;pageLayout&gt;.
    It adds a prebuilt set of tabs and global buttons, and
    provides a new &lt;topHead&gt; child element.
 </documentation>

  ...
</templateDefinition>

In addition, <documentation> elements can be added as children of <attribute> and <namedChild> elements to document these entries directly:

 <type base="ui:pageLayout">
   <namedChild name="topHead">
     <documentation>
        will be added above the page's contents
     </documentation>
   </namedChild>
   <attribute name="selectedTab" javaType="int">
     <documentation>
        the index of the selected tab
     </documentation>
   </attribute>
 </type>

"Complex" Attributes

The terminology of attributes in UIX can cause a great deal of confusion. When we discuss attributes for templates, we technically mean UIX Components UINode attributes, not XML attributes. For simple types - like strings, integers, characters, and Booleans - the two terms are one and the same. But for more complex Java types - DataObjects and DataObjectLists, for instance - a single XML attribute isn't sufficient to represent the full type. Consequently, for these types, we use a child element instead of an attribute. For example, take the following template definition:

<templateDefinition xmlns="http://xmlns.oracle.com/uix/ui"
          xmlns:ui="http://xmlns.oracle.com/uix/ui"
          xmlns:data="http://xmlns.oracle.com/uix/ui"
          targetNamespace="http://www.example.org/demo/templates"
          localName="someWidget">

 <type>
   <attribute name="someData"
                  javaType="oracle.cabo.ui.data.DataObject"/>
 </type>

 <content>
     ...
 </content>
</templateDefinition>

The "someData" attribute would actually be written to in UIX as:

 <demoTmps:someWidget>
   <demoTmps:someData key1="value1" key2="value2"/>
 </demoTmps:someWidget>

(For information on the syntax for defining DataObjects and DataObjectLists in UIX, see the Data Binding chapter).

As if this all wasn't confusing enough, "complex attributes" can still be databound. Even though they have to be specified with elements when they're set to fixed values, they're data bound as if they were normal attributes:

 <demoTmps:someWidget data:someData="key@aDataObject"/>

Using Java Code Inside Templates

At some point, you may very well find that some parts of your template UI can't be expressed in UIX. If you do, let us know - there may be some missing, generic functionality that'll get you that last mile. But you can integrate Java code inside your templates using UIX Components DataProviders and the UIX <dataScope> element.

<dataScope> elements inside of templates are automatically scoped. You can add all the <dataScope> elements you want and be sure that they will not affect the UIX of any developer using that template. So, for example, you don't need to make the name attribute of a <data> element unique.

The Java code backing these DataProviders is just like any other DataProvider, so you'll use all the usual techniques for creating DataObjects. There is one trick you'll need to learn, though, if you want to get attributes off of the template element. For example, say you've defined a template element "foo" with a "disabled" attribute:

<?xml version="1.0" encoding="UTF-8"?>
<templateDefinition xmlns="http://xmlns.oracle.com/uix/ui"
          xmlns:data="http://xmlns.oracle.com/uix/ui"
          targetNamespace="http://www.example.org/demo/templates"
          localName="foo">

 <type>
   <attribute name="disabled" javaType="boolean"/>
 </type>

 <content>
   <dataScope>
     <provider>
       <data name="someData">
         <method class="..." method="getTemplateData"/>
       </data>
     </provider>
     <contents>
       ...
     </contents>
   </dataScope>
 </content>

Now, inside your Java code, you want access to that "disabled" attribute. To get it, you'll need to use two RenderingContext methods you've probably not used before: getParentContext() and getAncestorNode():

  static public DataObject getTemplateData(
    RenderingContext context,
    String           namespace,
    String           localName)
  {
    // Get the parent rendering context
    RenderingContext parentContext =
      context.getParentContext();  
  
    // Get the template UINode
    UINode templateNode =
      parentContext.getAncestorNode(0);

    // Get the value of DISABLED - note that we use
    // the parent context
    Object disabledObj =
      templateNode.getAttributeValue(parentContext,
                                     DISABLED_ATTR);
    boolean disabled = Boolean.TRUE.equals(disabledObj);

    DataObject someDataObject = ...;
    
    return someDataObject;
  }

The reasons for this are a bit too involved to get into here. Succinctly, the template renders using its own RenderingContext to avoid interfering with the outer components. But you don't have to understand the full details to follow this recipe.

The rendered Attribute - a Templating Caveat

The rendered attribute is a rather special UIX attribute. Unlike all others, it's used not by the component itself, but instead by the parent. This is because the parent has to know whether or not to reserve layout space for that child. For example, in a <pageLayout>, the advertising images get arranged very differently depending on whether they are all present or only one is present. It's the <pageLayout> itself that's responsible for arranging the advertisements, so it has to know if they'll be rendered at all.

This means that "rendered" must be set on the instantiation of the template, not internally to the template definition itself.

For example, say you wanted to have a page layout template with a train that is sometimes disabled and sometimes not. You might try to write this sort of a template:

<templateDefinition ...>
  ...
 <content>
   <train data:rendered="doWeNeedATrain@aPrivateDataSource">
     <contents>
         ...
     </contents>
   </train>
 </content>
  ...
</templateDefinition>

...and then use this template inside a <pageLayout>:

 <pageLayout>
   <location>
     <myNS:myTrain/>
   </location>

    ...
 </pageLayout>

It seems like this should work - but it won't. <pageLayout> will ask if the <myNS:myTrain> is rendered - not if its content is rendered. So you'd be forced to write instead:

 <pageLayout>
   <location>
     <myNS:myTrain data:rendered="doWeNeedATrain@aNotSoPrivateDataSource"/>
   </location>

    ...
 </pageLayout>

In this example, you could preserve your abstraction by building this logic into a <pageLayout> template:

<templateDefinition ...>
  ...
 <content>
   <pageLayout>
     <location>
       <myNS:myTrain data:rendered="doWeNeedATrain@aPrivateDataSource"/>
     </location>>
      ...
   </pageLayout>
 </content>
  ...
</templateDefinition>

Templates and Data Binding

Templates interact subtly with databinding, and you should be aware of the rules as you write templates.

A major goal of the template feature is that developers and designers using a template-based element should not have to know the element is implemented using templates. This means that the template should not have any side effects. So, a subtlety: <dataScope>s used inside a template are not visible outside the template! An example may help here. First the UIX file, then the template it uses:

<page xmlns="http://xmlns.oracle.com/uix/controller"
      xmlns:data="http://xmlns.oracle.com/uix/ui"
      xmlns:demoTmps="http://www.example.org/demo/templates">

 <templates xmlns="http://xmlns.oracle.com/uix/ui">
   <templateImport source="templateData.uit"/>
 </templates>

  <content>
    <dataScope xmlns="http://xmlns.oracle.com/uix/ui">
      <provider>
        <data name="commonName">
          <inline value="Outer text"/>
        </data>
        <data name="rareName">
          <inline value="Rare text"/>
        </data>
      </provider>
      <contents>
        <demoTmps:styledText data:text="value@commonName"/>
      </contents>
    </dataScope>
  </content>
</page>
<?xml version="1.0" encoding="UTF-8"?>
<templateDefinition xmlns="http://xmlns.oracle.com/uix/ui"
          xmlns:ui="http://xmlns.oracle.com/uix/ui"
          xmlns:data="http://xmlns.oracle.com/uix/ui"
          targetNamespace="http://www.example.org/demo/templates"
          localName="styledText">

 <!-- define the template's type information -->
 <type base="ui:styledText">
 </type>

 <!-- define the content of the page -->
 <content>
   <dataScope>
     <provider>
       <data name="commonName">
         <inline value="Inner text"/>
       </data>
     </provider>
     <contents>
       <styledText data:text="value@commonName"/>
       <styledText>
         <attributeMap><rootAttributeMap/></attributeMap>
       </styledText>
       <styledText data:text="value@rareName"/>
     </contents>
   </dataScope>
 </content>
</templateDefinition>

If you run that example, you'll see the following output:

Inner textOuter textRare text

All of the actual content gets output inside the inner template, so let's walk through the template contents. Consider the first <styledText> in the template:

       <styledText data:text="value@commonName"/>

This resolves to "Inner text," which makes sense looking at this .uit file - the one <dataScope> defines the commonName data. But the UIX file that uses this template also defines "commonName". So, the first rule: databinding inside a template gives priority to the data defined in the template.

Now, the second <styledText>:

       <styledText>
         <attributeMap><rootAttributeMap/></attributeMap>
       </styledText>

This is a more typical template line. Recall that the <attributeMap>, etc., line says, "take all of the attributes defined on the parent element in the UIX file and add them to this <styledText>. Therefore, this <styledText> should write out the "text" attribute of the parent element that is defined in the UIX file as:

        <demoTmps:styledText data:text="value@commonName"/>

But how on earth does this result in "Outer text"? The template defines "value@commonName" to be "innerText"! Well, this is the really key point - the "data:text" defined on <demoTmps:styledText> has to evaluate independently of how the template is implemented. So the databinding in the UIX file can't even see the <dataScope> defined in the template, but instead sees only its own <dataScope>. Rule number two: Databinding outside of the template always ignores databinding defined inside of the template.

For completeness sake, the last <styledText>:

       <styledText data:text="value@rareName"/>

This results in "Rare text." The only databinding for the rareName data is in the outer UIX. So, rule number three: databinding inside a template can see data defined outside of the template.

Here are those three rules again:

  1. Databinding inside a template gives priority to the data defined in the template.
  2. Databinding outside of the template always ignores databinding defined inside of the template.
  3. Databinding inside a template can see data defined outside of the template.

Grouping Templates

Once you've developed a whole set of templates, it'll begin to be tedious including all of them in each file and maintaining a long list of <templateImport> elements. UIX supports a <templateLibrary> element that lets you group templates together and include them with a single <templateImport>.

The syntax is simple. In one more .uit file, you'll define a single <templateLibrary> element and explicitly import all of the templates you want to group together:

File library.uit

 <templateLibrary xmlns="http://xmlns.oracle.com/uix/ui">
   <templateImport source="firstTemplate.uit"/>
   <templateImport source="secondTemplate.uit"/>
   <templateImport source="thirdTemplate.uit"/>
 </templateLibrary>

Then, just import the library file anywhere it is needed.

 <templates xmlns="http://xmlns.oracle.com/uix/ui">
   <templateImport source="library.uit"/>
 </templates>

Template libraries are a bit like using "wide imports" in Java:

  import oracle.cabo.ui.*;

But, unlike wide Java imports, you don't run into ambiguity problems, because you'll still explicitly specify the namespace of each UIX element when they're used.

Templates, UIExtensions, and Java-based UIs

All this is great - if you're using XML to build your user interfaces. But it doesn't seem to help you if you're using our Java API. What's more, templates look like second class citizens even if you're using XML - you need to import the template files in every file. Thankfully, there's a solution at hand - the UIExtension interface.

The UIExtension interface is a very simple interface that lets you bundle up rendering and parsing behavior for an extension in a single object. And the oracle.cabo.ui.composite.TemplateUIExtension and oracle.cabo.ui.composite.TemplateLibrary classes provide a simple way to use your .uit files as extensions - at which point they're first class parts of UIX that can be used in your XML without imports, even from Java.

Turning templates into a UIExtension is a three step process:

  1. First, group your templates into <templateLibrary> files.
  2. Second, in your Java code, create a TemplateLibrary object. Say that your templates are in a "library.uit" file in the "c:\foo\" directory:
      // Create a "NameResolver", which is used
      // to locate the library and any files it includes
      NameResolver resolver = new DefaultNameResolver(new File("c:\\foo\\"), null);
      // Create the library - see the Javadoc for information on this function
      TemplateLibrary library = new TemplateLibrary(resolver,
                                                    "library.uit",
                                                    null, null, null);
  3. Third, create the TemplateUIExtension:
      UIExtension extension = new TemplateUIExtension(library);

A UIExtension can be registered on a LookAndFeelManager or a ParserManager:

  // Register the rendering half of the extension
  LookAndFeelManager.getDefaultLookAndFeelManager().
     registerUIExtension(extension);
  
  // Register the XML parsing half of the extension
  ParserManager manager = ...;
  extension.registerSelf(manager);

But there is an easier way. Just use the <template-library> element in uix-config.xml.


 <?xml version="1.0" encoding="ISO-8859-1"?> 
 <configurations xmlns="http://xmlns.oracle.com/uix/config">
   <application-configuration>
     <ui-extensions>
       <template-library>c:\foo\library.uit</template-library>
       <template-library>aRelativePath/library/library.uit</template-library>
     </ui-extensions>
   </application-configuration>
 </configurations>

For more information on uix-config.xml, see the Configuration chapter.

If you're a Java-only programmer using our beans, all that really matters is registering the rendering half of the UIExtension, which is handled by the call to LookAndFeelManager.registerUIExtension(). (In other words, you don't need to bother with registering the templates on the ParserManager.) Once you've registered the TemplateUIExtension, you can use those components directly from Java! You just need to create a bean with the right namespace and name. For example, if we'd registered the demoPageLayout.uit file, the following Java code would gain access to that component:

  // Create a DemoPageLayout bean
  BaseWebBean bean = new BaseWebBean("http://www.example.org/demo/templates",
                                     "demoPageLayout", null);

  // Set "selectedTab"
  bean.setAttributeValue(AttributeKey.getAttributeKey("selectedTab"),
                         new Integer(1));
  

Now, this is nice, but what you really wanted to write is:

  // Wouldn't this be better?
  DemoPageLayoutBean bean = new DemoPageLayoutBean();
  bean.setSelectedTab(1);

You can write the DemoPageLayoutBean class yourself, but that's a pain. In the future, UIX will provide tools to automatically convert a .uit file into the corresponding Java source code for a bean class. We'll also be providing tools to convert from .uit files into XML Schema .xsd files, so you'll be able to verify your UIX and use the templates in the schema-driven XML editor in JDeveloper9i.

Templates are first class citizens in UIX Components: if you want, you can think of them as an easy way to build UIX Components components without writing code.

Why Use <include>?

Since templates provide so much functionality above and beyond what <include> can do, why use <include> at all? There are a few reasons.

First, <include> is really simple. Not that it's so hard to use a template - but writing an included UIX file is much easier than writing a template file.

Second, <template> is UIX Components-only. Now, it can use ctrl: attributes - like ctrl:event, etc., but it can't specify event handlers. (As we noted above, UIX 2.0.5 ignores event handlers specified on included pages - but this restriction will be lifted, while templates can never support event handlers).

Third, templates can sometimes be too opaque. Remember that a template element looks like just a single UINode to the including file. But an <include> looks like its whole tree of UINodes. This has rendering implications - for example, at this time, <header> elements defined inside of a <templateDefinition> are ignored by quicklinks.

Including JSP, Servlet, and Other HTML Content

UIX offers an additional form of includes to merge in non-UIX content. UIX can include Servlets and JSPs, as well as including HTML directly.

To include Servlets and JSPs, use the <servletInclude> element (accessible in Java via the ServletIncludeBean). It supports two attributes, but most developers will just use "source"; this is the value you'd use for the "page" attribute for the JSP <jsp:include> tag. For example, if you wrote in a JSP:

 <jsp:include page="foo.jsp"/>

Then you'd write in UIX:

 <servletInclude source="foo.jsp"/>

The <servletInclude> element can include both servlets and JSPs. (Exception: when used with the combination of JSP and Apache JServ, it can only include JSPs.) However, it can't include an arbitrary URL - for example, you can't grab the contents of some HTML stored on a different server. The <urlInclude> element lets you do just that:

 <urlInclude source="http://www.example.org/somefile.html"/>

UIX will automatically copy some of the required HTTP request headers - like User-Agent - and explicitly set others, like Accept-Language. Because the HTML will be inserted as is, you'll generally not want to insert an entire HTML file. Usually, the target should only be a snippet of HTML. (We may in the future add filtering to automatically strip out <HTML>, <HEAD>, and <BODY> tags, convert relative HREF attributes to absolute, etc.)

Conclusion

Once you've mastered includes and templating, you should be able to build much more modular, reusable UIX, improving developer productivity and application customizability.