Adding Components to an XPage Programmatically
Sun Jul 19 09:16:35 EDT 2015
One of my favorite aspects of working with apps using my framework is the component binding capability. This lets me just write the main structure of the page and let the controller do the grunt work of creating fields with validators and converters. There's a lot of magic behind the scenes to make it happen, but the core concept of dynamic component creation is relatively straightforward.
An XPage is a tree of components, and those components are all Java objects on the back end, which can be manipulated and added or removed programmatically. To demonstrate, I'll start with this basic XPage:
<?xml version="1.0" encoding="UTF-8"?> <xp:view xmlns:xp="http://www.ibm.com/xsp/core" beforePageLoad="#{controller.beforePageLoad}" afterPageLoad="#{controller.afterPageLoad}"> <xp:div id="container"> </xp:div> </xp:view>
Now, I'll add a basic form table using the afterPageLoad
method in the controller class:
package controller; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import com.ibm.xsp.component.UIViewRootEx2; import com.ibm.xsp.component.xp.XspInputText; import com.ibm.xsp.extlib.component.data.UIFormLayoutRow; import com.ibm.xsp.extlib.component.data.UIFormTable; import com.ibm.xsp.extlib.util.ExtLibUtil; import com.ibm.xsp.util.FacesUtil; import frostillicus.xsp.controller.BasicXPageController; public class home extends BasicXPageController { private static final long serialVersionUID = 1L; @SuppressWarnings("unchecked") @Override public void afterPageLoad() throws Exception { super.afterPageLoad(); UIViewRootEx2 view = (UIViewRootEx2)ExtLibUtil.resolveVariable(FacesContext.getCurrentInstance(), "view"); UIComponent container = FacesUtil.findChildComponent(view, "container"); UIFormTable formTable = new UIFormTable(); formTable.setFormTitle("Some Form"); formTable.setStyle("margin: 2em; width: 20em"); container.getChildren().add(formTable); formTable.setParent(container); UIFormLayoutRow formRow = new UIFormLayoutRow(); formRow.setLabel("Name"); formTable.getChildren().add(formRow); formRow.setParent(formTable); XspInputText inputText = new XspInputText(); formRow.getChildren().add(inputText); inputText.setParent(formRow); } }
There are a few concepts to get a handle on here, but fortunately they're not as esoteric as other aspects of back-end XPages development.
To start out with, there's the question of how you're supposed to know what classes the components are. The best way to find this out is to create a basic XPage containing the control with an ID and then go to the Package Explorer view, then the "Local" source folder, and find the class file for your page in the "xsp" package. In there, you can see the code that actually generates the XPage (which is doing basically the same thing as we're doing here). Look for the ID you gave the control on the page and you can find the class behind it.
Next is the job of finding the parent control you're going to attach the new components to. In this case, I used the FacesUtil
class to search for the component by its ID, because I know it's the only one on the page with that base ID. This tack will usually do the trick for you, but there are other ways to find it, such as binding or XspQuery.
Finally, there's the need to both add the new child to the parent's children list and to set the parent in the child itself. This is a bit of housekeeping boilerplate, but it has to be done.
Once you have this code running, you get a basic form (using the Bootstrap3.2.0 theme in this case):
This can get much more in-depth: anything you can do declaratively in the XPage XML, you can do programmatically in Java. You can also manipulate existing components: change their properties, rearrange them, or remove them from the tree entirely. I've found knowledge of this to be both useful as a part of the toolbox as well as a way to really clear up the mental model of what's going on on the page.