Inspect Component Properties using Component Binding
Inspired by Paul Withers recent component binding post, I discovered another use for component binding whilst I was preparing for my upcoming presentation ‘Anatomy of a UI Control’ this Thursday at AUSLUG.
In my presentation I demonstrate how to determine a component’s Component Family and it’s Renderer Type which are both essential to determine which renderer is selected for the component by XPages.
It is extremely similar purpose to a technique that was previously demonstrated by the late Tim Tripcony on his blog. Tim demonstrated how to determine a component’s StyleKitFamily, which is an important setting when applying theme properties.
All I have done is rejig the concept to use component binding to link the component to the computed values which output the properties.
In this example I am inspecting the settings of the widget Container, however if you want to inspect a different component, then remove the widgetcontainer, replace it with whatever component you want to inspect, and make sure the ‘binding’ property is set the same as it was with the widgetcontainer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
<?xml version="1.0" encoding="UTF-8"?> <xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xe="http://www.ibm.com/xsp/coreex"> <!-- Bind the Component that you want to inspect to a request scope variable by using the binding property We Will use myComponent as scope variable name --> <xe:widgetContainer id="widgetContainer1" titleBarText="Hi there" binding="#{requestScope.myComponent}"> </xe:widgetContainer> <!-- Output the properties of that Component --> <xp:table> <xp:tr> <xp:td> <xp:label value="Component Family" id="label1"></xp:label> </xp:td> <xp:td> <!-- Output the Components Component Family using getFamily() --> <xp:text escape="true" id="computedField1" value="#{requestScope.myComponent.family}"></xp:text> </xp:td> </xp:tr> <xp:tr> <xp:td> <xp:label value="Renderer Type" id="label2"></xp:label> </xp:td> <xp:td> <!-- Output the Components Renderer Type using getRendererType() rendererType in EL Notation --> <xp:text escape="true" id="computedField2" value="#{requestScope.myComponent.rendererType}"></xp:text> </xp:td> </xp:tr> <xp:tr> <xp:td> <xp:label value="StyleKit Family" id="label3"></xp:label> </xp:td> <xp:td> <!-- Output the Components Style Kit Family using getStyleKitFamily() styleKitFamily in EL Notation --> <xp:text escape="true" id="computedField3" value="#{requestScope.myComponent.styleKitFamily}"></xp:text> </xp:td> </xp:tr> </xp:table> </xp:view> |
When the Bootstrap theme is applied to the application, the properties are as follows:
Obviously with different themes applied you might get different renderer types for the same component.
I have added it as an XSnippet
At the end of the day, this is not really groundbreaking stuff! You could achieve the same result beforehand with a few SSJS computed values as follows:
1 2 |
var c = getComponent("widgetContainer1"); return c.getComponentFamily(); |
However if you are weird like me and prefer to use EL because it looks nicer in the markup, then using the Component Binding might be an option for you!
Worth emphasising here is using requestScope for the binding. The natural inclination is viewScope, because it needs to be accessible for the life of the page. But you’re right that requestScope is best, because I guess nothing is stored in the requestScope except a pointer to the component. And because the component tree is reloaded for each request, the binding will get reprocessed and the pointer added to reqeustScope again. It’s similar to variables within Panels or Repeats, because they too get added to requestScope (temporarily).
Yes exactly! I have previously run into the problem that if you use viewScope, the UIComponent is not serializable.
This is not a problem with in-memory view persistence, but IS a problem if disk persistence is in place and throws the ‘not serializable’ error when it tries to serialize to disk.
If you bind it to a managed bean in viewScope you can mark the ‘bound’ field transient and it won’t attempt to serialize it. I think you covered that in one of your posts?
There is another problem even when using a transient field in a managed bean to bind a UIComponent to it: The component tree might not release correctly, which can lead to memory problems and/or weird behaviour of an application.
The golden rule is not to bind a UIComponent to a managed bean, regardless of their scope.
Link For more details: https://technology.amis.nl/2013/12/16/adf-classic-mistakes-and-worst-practices-abstract-from-ukoug-2013/
It is even shorter to use the local EL scope with binding=”#{myComponent}”. Then you can access it with value=”#{myComponent.styleKitFamily}”.
Great tip! how come I never new about local scope? definitely will be using this!
Interesting stuff. I did not even know that the Widget Container existed. Has anyone wrote about them?
Hi Csaba, I haven’t seen anyone blog about it but they are described in the XPages extension library book, and also there are some examples in the Extension Library Demo Database.
They are quite handy to use in left/right side panels