Search code examples
dojoxpages

How can I change the styleClass of a control while also doing a partial refresh on another component?


I have a straightforward XPage that lets a user answer a question with a simple Yes/No/NA response using radio buttons. I have restyled the radio buttons to look like a bootstrap button group to make it visually more interesting for the user. If the user chooses "Fail" then they are informed that they need to do something else - easily done with a simple partial refresh to a div further down the page.

This all works fine.

The problem I'm having is that I'd like it so that when the user selects an option, I would like to add a new class of "active" to the selected option so that it highlights in a pretty colour. But for the life of me I can't get this to work and though I'm sure it's a straight forward problem, I can no longer see the wood for the trees.

My current (abridged) iteration of the radio button code is this:

<xp:div styleClass="btn-group item-result" id="edit-result" loaded="${Question.open}">          
    <xp:radio text="${lbl.kwPass1}" id="itemPass"
        styleClass="btn btn-pass #{(item.itemResult eq '0')?'active':''}" groupName="itemResult"
                selectedValue="1">
        <xp:eventHandler event="onchange" submit="true"
                    refreshMode="partial" refreshId="actionAlert">
            <xp:this.script><![CDATA[XSP.partialRefreshPost('#{id:edit-result}');]]></xp:this.script>
        </xp:eventHandler>
    </xp:radio>

<!-- other radio buttons -->
</xp:div>

<!-- other page compenents -->
<xp:panel id="actionAlert">
    <!-- panel content and appropriate rendered value -->
</xp:panel>

This was attempting to do a chained partial refresh on the radio button container so that the EL would evaluate and apply/remove the 'active' style based on the document datasource ('item') value. I have also tried using dojo.addClass, dojo.removeClass, XSP.partialRefreshGet and other options. I don't mind what the solution is as long as it's efficient and works. I'd prefer not to move the actionAlert panel to within the same container as the radio buttons and I can't do a full page refresh because there are other fields which will lose their values.

Some notes:

  • I'm not using a RadioGroup control because it outputs a table and I haven't got around to writing my own renderer for it yet. Single Radio button controls work fine for what I need them to do.
  • I originally tried using the full Bootstrap solution of using "data-toggle='buttons'" (source) which sorts out applying the "active" style fine but then, inevitably, prevents the partial refresh from working.
  • the radio button styles are clearly not Bootstrap standard

Any assistance pointers or, preferably, working solutions would be appreciated.


Solution

  • Stepping away from the problem, a couple of beers and an episode of iZombie later, I realized what I was doing wrong and sorted it out. So, for posterity, here is the simple solution that I swear I tried earlier but clearly works now:

    <xp:div styleClass="btn-group item-result" id="edit-result" loaded="${Question.open}">          
        <xp:radio text="${lbl.kwPass1}" id="itemPass" value="#{item.ItemResult}"
            styleClass="btn btn-pass" groupName="itemResult" selectedValue="1">
            <xp:eventHandler event="onchange" submit="true" refreshMode="partial" refreshId="actionAlert">
                <xp:this.script><![CDATA[dojo.query('.item-result > .btn').removeClass('active');
                        dojo.query('.btn-pass').addClass('active');]]></xp:this.script> 
            </xp:eventHandler>
        </xp:radio>
    <!-- et cetera -->
    

    The many places I was going wrong:

    1. In my code in the question, I was calling XSP.partialRefreshPost in the CSJS script of the radio button when it should have been in the onComplete of the eventHandler. It has to be chained to another partial refresh so that it runs after it, not at the same time. I did end up getting this right - but overlooked something I'll come to in point 3.

    2. In my original attempt to use Dojo, my first mistake was to try and target the ID of the radio button, something like:

      dojo.addClass(dojo.byId('#{id:radio2}'),'active');

    This actually works as expected, so long as you remember that the ID of the radio button on the XPage refers to the actual radio button control and not the label wrapping; and the label is what I wanted to style. So the class "active" was being actually being added, just not to the element I thought it was. I should have spotted this in my browser code inspector except for the third thing I got wrong:

    1. Ironically, I sorted out the first issue, remembering to put the XSP.partialRefreshPost into the onComplete - and then didn't remove it when trying to run the Dojo.addClass(). So I didn't notice the mistake with the addClass targeting the wrong element because after it ran, the partial refresh updated the container and removed the class I had just added which made me think that nothing was working.

    So now I have some neatly styled radio buttons that don't look like radio buttons and it's all managed client side without any unnecessary partial refresh trips to the server barring the one where I actually need to look stuff up from the server. And the vital lesson is - sometimes you just need to step away from a problem and come back to it with fresh eyes later on.