Suppose you have an XPage with a repeat control on it to show a list of documents. In the repeat you generate an <xp:button> for every document. Based on the status of the document you decide to give the button a different color, so you make the styleClass property of the button computed:
<xp:repeat id="repeat1" rows="30" var="entry"> <xp:this.value><![CDATA[#{javascript: print("computing repeat value"); return ["style1", "style2"]; }]]></xp:this.value> <xp:button value="Label" id="button"> <xp:this.styleClass><![CDATA[#{javascript: print("computing styleClass:"); print( typeof entry); return entry }]]></xp:this.styleClass> </xp:button> </xp:repeat>
That’s (roughly) the code you’d have to write. And guess what: it all works. Everyone’s happy!
So you decide to enhance the looks of your application and include Bootstrap. Bootstrap requires the btn class on all buttons for styling. Being an expert XPage developer you create a theme for your application, add the Bootstrap CSS file and add a control definition to give all buttons that btn class:
<control> <name>Button.Command</name> <property mode="concat"> <name>styleClass</name> <value>btn</value> </property> </control>
You reload the page, thinking you’re done and guess what: errors!
That’s roughly what I’ve been looking at today and at some point thinking I lost my sanity.
The print() statements in the code above were added by me to show the order of execution. Without the <control> definition in the theme I saw this on the server console:
> computing repeat value > computing styleClass: > string > computing styleClass: > string
Exactly what I expected! But… after adding the <control> definition I saw this:
> computing styleClass: > undefined > computing styleClass: > undefined > computing repeat value
Notice the subtle, yet very important, difference in the order of computing: with the <control> definition the styleClass is computed before (!) the repeat value, making it impossible to let the button style depend on the repeat value. This behaviour is caused by the mode=”concat” part, that should combine the value of the styleClass with the one set in the theme.
The Bootstrap4XPages plugin has that same <control> definition in it, so if you’re using that you will likely run into this some day.
The solution I came up with for now is to add a new <control> definition to the theme, setting it to override any inherited settings and setting the mode to the default (‘override’). Set the themeId to the new <control> name and it all works again:
<control> <name>Button.NoConcat</name> <property> <name>styleClass</name> <value>btn</value> </property> </control>
and:
<xp:button value="Label" themeId="Button.NoConcat"></xp:button>
I have no idea why it behaves this way and would have never guessed that a <control> breaks anything but the UI in my app. If anyone can share some light on this: please do!
I think it’s related to the timing of insertion for theme-based expressions. If you set repeat control as (repeatControls=”true”) and change styleClass computation to ‘$’, it should work.
(Credits to Tim Tripcony, explained this whole mechanism in a great detail…)
Great explanation of this interesting behavior.
I came across this recently when working with alternating row style based on the index of the repeat collection. When I added the a class via the theme to concatenate the property, the page started failing when trying to refer to the repeat index in the styleClass computation script.
Great post Mark and thank you for sharing. Ran into a similar issue, where I wanted to set the class of a button based on the value of a scope variable. I didn’t run into any error messages but the style class wouldn’t change.
I posted my issue on and thanks to Thomas Adrian, I got a solution for it.