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

12. Dynamic Structure for UIX Pages

By now, you should be pretty used to the UIX data binding model, which lets you change attribute values from one render to another. But how do you handle changes in structure? What if you need a section of your page to be hidden or shown? How can you repeat a section over and over, each time with different dynamic data? And how can you integrate Java code into your UIX, to handle the most demanding dynamic pages? This chapter will teach you about the features UIX supports that make all these options easy.

This chapter assumes you have a solid knowledge of the core features of the UIX framework. In particular, it's essential that you understand the Data Binding chapter.

This chapter contains the following sections:

The rendered Attribute

The very simplest feature for dynamic pages is the "rendered" attribute. This attribute is supported on all UIX Components UINode classes and each corresponding UIX element. When set to "false", that element - and all of its children - are simply ignored. For example, the following UIX will output "UIX is cool.":

 <flowLayout>
   <contents>
      UIX is
     <styledText rendered="false" text="not "/>
      cool.
   </contents>
 </flowLayout>
Very simple - and when data bound, this lets you turn on and off small or large parts of your page with a single variable:
 <table data:rendered="hasTablePermission@permissions">
   <contents>
      ...
   </contents>
 </table>

This is both powerful and simple, so try rendered before trying some of the more complicated techniques described later in this chapter.

The <table> Bean and rendered

A caveat needs to be mentioned with using rendered and the columns of a <table>. If you're familiar with that component, you'll remember that each element inside of its <contents> element defines a column. It's entirely legal to use rendered on these elements, and it's legal to databind rendered - but not to databind it to the current (per row) DataObject:

 <table>
   <contents>
     <!-- Legal -->
     <styledText rendered="false" ... />

     <!-- Also legal -->
     <styledText data:rendered="foo@someName" ... />

     <!-- NOT legal -->
     <styledText data:rendered="perRow" ... />
   </contents>
 </table>

You'd probably try this if you wanted to change the contents of a column from one row to the next. Thankfully, there's a simple workaround - or, actually, two workarounds. A very good solution is using the <switcher> element (described in the next section). But, if you want to use "rendered", take all of the possible elements that should go into that column, and wrap them inside of a <column> element:

 <table>
   <contents>
     <!-- Legal -->
     <styledText rendered="false" ... />

     <!-- Now legal -->
     <column>
       <contents>
         <styledText data:rendered="perRow" ... />
         <styledText data:rendered="otherPerRow" ... />
       </contents>
     </column>
   </contents>
 </table>

The<switcher> Element

The "rendered" attribute is great for turning on and off one part of a page, but it's a pain if you always want to use one of several options - if you had 10 possibilities, you'd have to provide ten true/false values from your DataObject. It'd be a lot easier to just expose a single value, like the name of the possibility to choose. This is where the UIX <switcher> element - and the UIX Components SwitcherBean comes in.

A <switcher> element has a single attribute: "childName". And it can only contain one type of element: <case>. Each <case> element has a single attribute, "name". When it's asked to display, it finds the child whose name matches the "childName" attribute and displays that child - and only that child. An example should make this all pretty clear; the following UIX will display "Second case":

 <switcher childName="second">
   <case name="first">
      <styledText text="First case"/>
   </case>

   <case name="second">
      <styledText text="Second case"/>
   </case>
 </switcher>

It's an error to use the same "name" twice, but it's perfectly legal to have a "childName" that doesn't match any "name"; the <switcher> just won't render anything.

An important point: <switcher> is a UINode element. So you can only use it where you could use any other UINode element - <styledText>, <flowLayout>, etc. You can't add a <switcher> element just anywhere in your UIX. For example, the following UIX is illegal, because the contents of a <data> element should be a DataProvider UIX element, not a UINode.

 <dataScope>
   <provider>
     <data name="...">
       <!-- ILLEGAL -->
       <switcher ...>
       </switcher>
     <data>
   </provider>
 </dataScope>

You may wonder: why don't we have a <default> element, just like the Java "switch" statement? UIX's data binding can handle this without adding extra syntax! If you databind a UIX attribute and also explicitly specify it, then the explicit value is used as a default:

 <switcher childName="defaultName" data:childName="...">
   <case name="first">
       ...
   </case>

   <case name="second">
       ...
   </case>

   <!-- A default choice -->
   <case name="defaultName">
       ...
   </case>
 </switcher>

If the "childName" found with data binding can't be resolved - that is, it ends up as "null", then UIX falls back on the explicitly specified value: "defaultName".

If you're writing Java code with UIX Components beans, you'll use the SwitcherBean class. Instead of <case> elements, you'll call setNamedChild. Here's that first UIX example, only in Java:

  SwitcherBean switcher = new SwitcherBean();
  switcher.setChildName("second");
  switcher.setNamedChild("first",
                         new StyledTextBean("First case", null));
  switcher.setNamedChild("second",
                         new StyledTextBean("Second case", null));

Now, the obligatory caveat. <switcher> works well in nearly all cases, but there are some element types where it causes problems. For example, we don't support wrapping a <switcher> around a set of <column> elements in a <table>; you have to put the <switcher> inside of the <column> element. This causes problems because the <table> looks for its <column> children, and doesn't see them (because they're covered up by the <switcher>). These cases are fairly obscure, but you should be aware they exist. The "rendered" attribute doesn't have this problem.

Iteration: the "childData" Attribute and DataObjectListNodeList

So far, you can turn and off pieces of your page. But these aren't enough to handle a very common case: repeating a section of your page. For example, say you want the following HTML:

Here, you want to stamp out the same bit of content several times, but each time with a different piece of data. The UIX "childData" attribute - and the UIX Components DataObjectListNodeList class - let you implement this.

To start, let's write the UIX for a single row:

 <tableLayout>
   <contents>
     <rowLayout>
       <contents>
         <styledText text="Name:"/>
         <textInput name="????" text="????"/>
         <cellFormat width="10"/>
         <styledText text="Phone number:"/>
         <textInput name="????" text="????"/>
       <contents>
     </rowLayout>
   </contents>
</tableLayout>

Now, we'll add in "childData" to get you the needed result. Unlike most attributes, you don't set "childData" directly on the row. Instead, you'll set it on the<contents> element. Another oddity - it doesn't support being directly set - you have to use databinding (as of UIX 2.0.3 - we may add a syntax for setting it directly in future releases). Here's the example, adding in "childData" and a <dataScope> to define where we're getting the data:

 <dataScope>
   <provider>
     <data name="source">
       <method class="SampleClass" method="getRows"/>
     </data>
   </provider>
   <contents>
     <tableLayout>
       <contents data:childData="rows@source">
         <rowLayout>
            ...
         </rowLayout>
       </contents>
     </tableLayout>
   </contents>
 </dataScope>

We'll show the contents of <rowLayout> and the getRows() method soon enough, but for now note that we've put the "data:childData" on the <tableLayout>'s contents, not the <rowLayout>; we're asking it to repeat the row layout, not the contents of the row layout. (If it were on the row layout's <contents> element, you'd see one very, very long row, not several identical rows.)

The Java type of "childData" must be a UIX DataObjectList. Remember - these are analogous to an array of DataObjects. For every DataObject in the DataObjectList, we repeat the contents once. If there's five DataObjects in rows@source, we'll have five rows. And each time you render, the "current" DataObject will be the next DataObject in that list. This is exactly the same mechanism that UIX uses for the <table> and its "tableData" attribute.

So, let's write our Java model. We'll use the UIX introspection layer to turn a simple Java class into DataObjects:

  import oracle.cabo.ui.data.DataObjectList;
  import oracle.cabo.ui.data.bean.BeanArrayDataObjectList;

  public class SampleClass
  {
    // For simplicity's sake, we're using public fields.
    // The introspection code can handle "get" methods too.
    public class Person
    {
      public String id;
      public String firstName;
      public String phoneNumber;
    }

    static public DataObject getRows(
      RenderingContext context, String ns, String name)
    {
      // Get all of the people in an array
      Person[] people = _getPeople(...);

      // Use introspection to turn it into a DataObjectList
      DataObjectList list = 
  new BeanArrayDataObjectList(people);

      // And return that one item in a DataObject
      return new DictionaryData("rows", list);
    }
  }

Now, we can write our <rowLayout> element:

  ...
 <tableLayout>
   <contents data:childData="rows@source">
     <rowLayout>
       <contents>
         <styledText text="Name:"/>

         <!-- text input: bind the "text" attribute
                  directly to the "firstName" property of our data.
                  For the "name", concatenate the ID and "Name" -->
         <textInput data:text="firstName">
           <boundAttribute name="name">
             <concat>
                name<dataObject select="id"/>
             </concat>
           </boundAttribute>
         </textInput>

         <cellFormat width="10"/>

         <styledText text="Phone number:"/>

         <!-- another text input - pretty much the same as before -->
         <textInput data:text="phoneNumber">
           <boundAttribute name="name">
             <concat>
                phone<dataObject select="id"/>
             </concat>
           </boundAttribute>
         </textInput>
       <contents>
     </rowLayout>
   </contents>
 <tableLayout>

Let's extract that first <textInput> and break it down:

         <textInput data:text="firstName">
           <boundAttribute name="name">
             <concat>
                firstName<dataObject select="id"/>
             </concat>
           </boundAttribute>
         </textInput>

The two points to notice about this example.

  1. The "text" attribute should just be the "firstName" property from the DataObject: the data:text="firstName" accomplishes that.
  2. The "name" attribute is trickier. It has to be unique on every row, but we want it to follow a consistent pattern from row to row. The simple technique we've used here uses string concatenation to glue the ID property and "firstName" together - so if the ID is "1155", the "name" attribute would be "firstName1155".

Finally, there's nothing about "childData" that requires you use only one element inside <contents>; if you have two elements, each will be repeated. In general, if you have M elements, and your DataObjectList has N entries, UIX will act as if you have M*N elements. So, for example, if we change our UIX to:

     <tableLayout>
       <contents data:childData="rows@source">
         <rowLayout>
           <styledText text="Name:"/>
           <textInput .../>
         </rowLayout>
         <rowLayout>
           <styledText text="Phone number:">
           <textInput .../>
         </rowLayout>
       </contents>
     </tableLayout>

then our HTML result will change to:

Instead of stamping out one row with four columns, we're now stamping out two rows each with two columns. And nothing's changed in our Java layer.

If you're developing with Java, you still have all of this functionality, but with a very different syntax. Instead of setting an attribute, you'll use the DataObjectListNodeList class. UINodeLists are the storage abstraction UIX uses for the indexed children of a UINode, and by replacing the UINodeList you can greatly alter a node's behavior. Here's our example, only written using UIX Components and Java.

import oracle.cabo.ui.collection.DataObjectListNodeList;
import oracle.cabo.ui.data.DataBoundValue;

...

    TableLayoutBean tlb = new TableLayoutBean();

    BoundValue rows = new DataBoundValue(_YOUR_NAMESPACE, _YOUR_NAME, "rows");
    tlb.setIndexedNodeList(new DataObjectListNodeList(rows));

    RowLayoutBean rowLayout = new RowLayoutBean();
    tlb.addIndexedChild(rowLayout);
...

A small point of caution: setting the UINodeList erases all of the indexed children of a node - so you'll want to call setIndexedNodeList() before adding any children.

Integration with Java: the <include> Element

The techniques you've seen so far can accomplish a lot. But they do eventually run into limits. You can repeat or turn off an element that's in the UIX tree, but you can't add an element that isn't in the tree at all!

Say, for example, you're trying to display and edit the results of an arbitrary SQL query. There's no way to know how many columns there'll be until you run the query, nor is there any way to know the type of each column. In Java, this wouldn't be a big deal - you'd just create the UINodes at runtime, and forget about caching. And it's fine to fall back on that technique for parts of a UIX application - there's nothing wrong with mixing purely Java-based pages with UIX pages. But it'd be nice to write as much of your page as possible in UIX, and only use Java for the pieces that absolutely need it. The <include> element (and the Java IncludeBean) make this possible.

You may have already seen <include> used to include one UIX Controller page inside another:

      <include ctrl:node="aPageName"/>

But, instead of asking UIX Controller to handle the include, we can use standard UIX databinding. "node" is really just another attribute - although in this case, its type is actually UINode! So, let's write a UIX page:

 <dataScope>
   <provider>
     <data name="source">
       <method class="SampleClass" method="getNode"/>
     </data>
   </provider>
   <contents>
     <flowLayout>
       <contents>
          UIX knows  
         <include data:node="javaNode@source"/>
       </contents>
     </flowLayout>
   </contents>
 </dataScope>

and some Java code:

  public class SampleClass
  {
    static public DataObject getNode(
      RenderingContext context, String ns, String name)
    {
      // Create a StyledTextBean
      UINode node = new StyledTextBean("includes!", null);
      // And return that one node in a DataObject
      return new DictionaryData("javaNode", node);
    }
  }

That's all there is to it. The page will act as if it had been written:

     <flowLayout>
       <contents>
          UIX knows  
         <styledText text="includes!"/>
       </contents>
     </flowLayout>

A very, very important note: like <switcher>, the <include> element is a UINode element. So it can only be added in places where another UINode element could go. Contrary to what some developers expect, <include> is not a lexical inclusion facility - it is strictly a UINode substitution layer.

You can actually use this same technique with an entirely UIX Components-based page. It's not as silly an idea as it may seem. Instead of recreating the entire page and all of its UINodes every time you render, you'll be able to keep most of the page cached and only recreate the part that varies. There are some complexities of using IncludeBean directly - UIX hides these for you - so you should read its documentation carefully before trying this.

Integration with Java: DeltaTree

The <include> element is good at gluing in new elements into a page. Together with databinding, the "rendered" flag, <switcher>, and "childData", you've got a fairly large toolbox. But we still haven't presented a way to take a static UIX file and arbitrarily change it in Java.

Since UIX is XML, one approach is to use any standard XML parsing API - DOM, JDOM, SAX, XSLT, etc. - to read in UIX, modify the XML, then pass the modified XML to UIX. This will work, and since you're getting your modifications in before our parser sees the UIX, you'll have unlimited freedom to mark up the source XML - or even generate from some source other than static XML. But if you want to use the UIX Components Java APIs, the DeltaTree API is what you need.

DeltaTree takes a UINode and lays differences - "deltas" - on top of it. Because it doesn't modify the underlying Java tree at all, it's completely thread-safe. The principle is straightforward: mark certain UINode elements with a "nodeID" attribute. (This attribute is used to identify nodes on the server - it doesn't get displayed on the client.) From Java, we'll find those nodes, wrap them in a proxy, then set attributes on the proxy. Then we render the proxy instead of the original tree. For the full details on this API, you'll want to consult our JavaDoc, but let's walk through a very simple example.

First, a simple UIX page. We'll find a single UINode and update its "styleClass" attribute to change its CSS style.

 <page xmlns="http://xmlns.oracle.com/uix/controller">
   <javaClass name="DeltaTreeTest"/>

   <content>
     <stackLayout xmlns="http://xmlns.oracle.com/uix/ui">
       <contents>
         <styledText text="One"/>
         <styledText text="Two"/>
         <styledText text="Three" nodeID="three"/>
       </contents>
     </stackLayout>
   </content>
 </page>

This is mostly straightforward, but note two things:

  1. We've defined a <javaclass> element; this will override the PageDescription Java class used to handle this page. For the purposes of this example, this is a straightforward way to hook up to DeltaTree.
  2. We've set a "nodeID" attribute on one of the three <styledText> elements. This will let us find this element from our Java code without trying to guess at the node hierarchy.

Now, for the Java code:

    package oracle.cabo.servlet.demo;

    import oracle.cabo.ui.MutableUINode;
    import oracle.cabo.ui.UINode;
    import oracle.cabo.ui.beans.StyledTextBean;
    import oracle.cabo.ui.path.DeltaTree;
    import oracle.cabo.ui.path.PathUtils;
    import oracle.cabo.ui.path.Path;
    import oracle.cabo.servlet.ui.DefaultUINodePageDescription;


    public class DeltaTreeTest extends DefaultUINodePageDescription
    {
      public UINode getRootUINode()
      {
        // Get the original root UINode
  UINode node = super.getRootUINode();

        // Wrap it in a DeltaTree
  DeltaTree tree = new DeltaTree(node);

        // Find the node we want
  Path path = PathUtils.findPathWithNodeID(null, node, "three");

        // Turn it into a MutableUINode
  MutableUINode nodeThree = tree.getMutableUINode(null, path);

        // Set the style class (using a static method, instead of casting)
  StyledTextBean.setStyleClass(nodeThree, "OraErrorText");

        // And render the DeltaTree, instead of the original root
  return tree.getRoot();
      }
    }

Let's walk through this code line by line.

  1. public class DeltaTreeTest extends DefaultUINodePageDescription

    This sets up our page description class. Page descriptions are used in UIX to encapsulate both the rendering and event handling parts of a page. We're extending the default class.

  2. public UINode getRootUINode()

    This method is responsible for returning the body UINode of the page. (A second method returns the node responsible for the HTML <head> section.)

  3. UINode node = super.getRootUINode()

    Get the static, unmodified root node of the page. This may or may not be the <stackLayout> element - it's unwise to rely on the structure being precisely preserved.

  4. DeltaTree tree = new DeltaTree(node);

    Wrap it in a DeltaTree. The original node is completely unmodified - but you should be careful to refer to the delta tree from this point on. It's also critical that you wrap the root in a delta tree - it's no good to wrap a child node in a DeltaTree, because the parent will still point to the original child.

  5. Path path = PathUtils.findPathWithNodeID(null, node, "three");

    A Path object is used in UIX Components to locate nodes in a tree. It stores every step that's used to get from the root down to the node - every getIndexedChild() or getNamedChild() call. The PathUtils class has utility methods for finding nodes with several criteria. That null that we've passed in is the RenderingContext - it's always legal to pass null, though you won't be able to get at the values of databound attributes without a rendering context.

  6. MutableUINode nodeThree = tree.getMutableUINode(null, path);

    Use the Path to get a mutable wrapper for the target node.

  7. StyledTextBean.setStyleClass(nodeThree, "OraErrorText");

    And set the attribute on the target. We're using a static method on StyledTextBean. You cannot cast the mutable node to a StyledTextBean - it is not an instance of that class.

  8. return tree.getRoot();

    Finally, ask the DeltaTree for its root - which will contain all the modified state. Since we haven't modified the original node object at all, you cannot just return node here - you wouldn't get any of the changes.

Not so tough. Of course, you can do a lot more than just set some attributes. You can do anything you would with UINodes you created yourself - add nodes, remove nodes, set UINodeLists, set up data binding, etc.

If you're a Java developer using the UIX Components beans directly, you can also use DeltaTree. Of course, since you've created the beans in Java, you can just directly modify those beans. But if you're trying to reuse bean trees created in one request for another request, DeltaTree lets you those beans trees in a thread-safe and memory efficient way.

Conclusion

With the basics of databinding and all the techniques you've seen in this chapter, and the you should now be able to create dynamic pages using uiXML and UIX Components.