2

How can I reference an <h:inputBox> from the <h:selectBooleanCheckbox> which are in a <ui:repeat> and part of a composite component so that I can disable and grey out an input box as depicted in the following screenshot?

enter image description here

<ui:repeat var="qAndA" value="#{cc.attrs.checklist.answer_attribute_list}">
   <td class="chklist"><h:selectBooleanCheckbox  onclick="toggleBox(this, qty_#{qAndA.attribute.attribute_id})" value="#{qAndA.na_value}"> </h:selectBooleanCheckbox></td>
   <td class="chklist"><h:inputText value="#{qAndA.quantity}" id="qty_#{qAndA.attribute.attribute_id}" /></td>

produces this HTML

<input id="j_idt48:2:j_idt94:j_idt94:j_idt167:j_idt171:1:qty_" type="text" name="j_idt48:2:j_idt94:j_idt94:j_idt167:j_idt171:1:qty_" value="6" />

My qty_#{qAndA.attribute.attribute_id} is getting truncated to qty_

I tried

<h:selectBooleanCheckbox  onclick="toggleBox(this, #{cc.attrs.id}_qty)"  value="#{qAndA.na_value}"></h:selectBooleanCheckbox>
<h:inputText value="#{qAndA.quantity}" size="3" maxlength="3" id="#{cc.attrs.id}_qty"/>

I am trying to refactor this old jsp to JSF:

Original JSP Include:

<SCRIPT>
   function toggleBox(checkName, quantityName) {
      if(checkName.checked) {
         quantityName.disabled=true;
         quantityName.style.backgroundColor="#808080"; 

      } else {
         quantityName.disabled=false;
         quantityName.style.backgroundColor="#FFFFFF"; 
      }
   }
</SCRIPT>

<c:forEach var="qAndA" items="${checklist.answer_attribute_list}">
<c:choose>
   <c:when test="${empty qAndA.na_value || qAndA.na_value == '0'}">
     <td class="chklist"><input type="checkbox" name="check_<c:out value="${qAndA.attribute_id}" />" onclick="toggleBox(this, qty_<c:out value="${qAndA.attribute_id}" />)" value="1"></td>
     <td class="chklist"><input name="qty_<c:out value="${qAndA.attribute_id}" />" type="text" value="<c:out value="${qAndA.quantity}" />" ></td>
  </c:when>

and produces this HTML

<td class="chklist"><input type="checkbox" name="check_1346788339807" onclick="toggleBox(this, qty_1346788339807)" value="1"></td>
<td class="chklist"><input name="qty_1346788339807" type="text" value="6" size="3" maxlength="3"></td>

Edit

quantity_checklist.xhtml composite component. the listener is called on the checklist.receiving_email but not when it is part of the iteration, whether I use datatable or ui:repeat

<p:selectBooleanCheckbox value="#{cc.attrs.checklist.receiving_email}">
    <p:ajax listener="#{checklistEdit.listenerTest()}" />
</p:selectBooleanCheckbox>
<h:dataTable var="qnA" value="#{cc.attrs.checklist.answer_attribute_list}">
    <p:column>
       <p:selectBooleanCheckbox value="#{qnA.na_value}">
      <p:ajax render="qty" listener="#{checklistEdit.listenerTest}" update="@all"/>
   </p:selectBooleanCheckbox>
 </p:column>
 <p:column>
    <h:inputText value="#{qnA.quantity}" id="qty" disabled="#{qnA.na_value}" />
 </p:column>
 </h:dataTable>

EDIT 2

recap: trying to get a dynamic array of checkboxes to disable and gray out in input box using composites but no luck unless I move code out of composites. Here is the code using composites (related to How to refactor snippet of old JSP to some JSF equivalent?):

WorkItem.xhtml

<ui:repeat var="actionItem" value="#{workItemController.wfiwi.work_action_list}">

<ui:fragment rendered="#{actionItem.workActionClass.workActionType.action_type_id == '2'}">
     <stk:dynamic_checklist actionItem="#{actionItem}" checklist="#{actionItem.meat}" />
</ui:fragment>

dynamic_checklist.xhtml composite

<ui:fragment rendered="#{cc.attrs.checklist.checkListClass.type == '3'}">
    <stk:quantity_checklist actionItem="#{cc.attrs.actionItem}" checklist="#{cc.attrs.checklist}" />
</ui:fragment>

quantity_checklist.xhtml composite is shown above in my first Edit

If I move the quantity_checklist code up into the WorkItem.xhtml file, the ajax update works and the Qty inputbox is greyed out and disabled. But this won't work using composite components.

Edit 3

see code below...The ajax update on the receiving_email checkboxes properly affects id ddd and poppy but the qnA.na_value ajax update not so. In other words, I check the N/A checkbox next to Qty 6 in screenshot, the listener fires and the primefaces message I set in the listener is shown. But the input box is still active and not greyed out. However, if I then toggle the receiving_email checkbox, it's listener is fired, header message appear, AND the QTY box next to the 6 in the screenshot now goes gray and is disabled.

<h:panelGroup id="ccc">
    <p:selectBooleanCheckbox value="#{cc.attrs.checklist.receiving_email}">
        <p:ajax listener="#{checklistEdit.listenerTest()}" update="ddd,poppy" />
    </p:selectBooleanCheckbox>
</h:panelGroup>
<h:panelGroup id="ddd">
    <h:outputText value="Email Checkbox:"></h:outputText>
    <h:outputText value="#{cc.attrs.checklist.receiving_email}" />
</h:panelGroup>

<h:dataTable var="qnA" value="#{cc.attrs.checklist.answer_attribute_list}" id="poppy">
    <p:column headerText="NA">
        <p:outputLabel id="navalue" value="#{qnA.na_value}"></p:outputLabel>
    </p:column>
    <p:column headerText="NA Checkbox">
        <p:selectBooleanCheckbox value="#{qnA.na_value}">
            <p:ajax listener="#{checklistEdit.checkBoxListenerTest}" update="poppy" />
        </p:selectBooleanCheckbox>
    </p:column>
    <p:column headerText="Quantity">
        <h:inputText value="#{qnA.quantity}" id="qty" disabled="#{qnA.na_value}" />
    </p:column>
</h:dataTable>
Community
  • 1
  • 1
jeff
  • 3,618
  • 9
  • 48
  • 101

2 Answers2

1

My qty_#{qAndA.attribute.attribute_id} is getting truncated to qty_ let alone all the other JSF ids prepended, which are because the composite component is a child of another composite component and both components are included conditionally using <ui:fragement>

No. It's because #{qAndA} is only available during view render time instead of view build time. The <ui:repeat> runs during view render time. If you had used <c:forEach>, then it would have worked the way you expected. But, as indicated by your previous question, you could due to being restricted to old Mojarra 2.1.7 unfortunately not use JSTL to dynamically build the view the way you want.

But after all, you don't need to manually make the IDs dynamic like that. Just specify a fixed ID.

<h:inputText id="qty" ... />

The <ui:repeat> will automatically prepend the iteration index in the ID. See also How to use EL with <ui:repeat var> in id attribute of a JSF component.


How can I reference an <h:inputText> from the <h:selectBooleanCheckbox> which are in a <ui:repeat> and part of a custom composite component so that I can disable and grey out an input box as depicted in the following screenshot

Just set the input's disabled attribute to checkbox's value and use JSF-provided ajax facility to update the input when the checkbox is toggled:

<h:selectBooleanCheckbox value="#{qAndA.na_value}">
    <f:ajax render="qty" />
</h:selectBooleanCheckbox>
...
<h:inputText id="qty" value="#{qAndA.quantity}" disabled="#{qAndA.na_value}" />

No additional JS needed. For styling, just put this in a normal CSS file:

input[disabled] {
    background-color: #808080;
}

Otherwise, if the input were initially disabled, and you re-enabled it using JavaScript without notifying JSF about this (via e.g. <f:ajax>), then JSF would as part of safeguard against hacked requests still deny the submitted value. See also How to disable/enable JSF input field in JavaScript?


Unrelated to the concrete problem, you appear to want to render a HTML <table> using <ui:repeat>. Have you considered using <h:dataTable> instead? It should reduce some HTML boilerplate. And, in that old Mojarra version, it has much more robust state saving while <ui:repeat> is known to have bugs here and there in complex views with ajax stuff.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • hmm. The checkbox isn't affecting the inputbox and I'm getting "The form component needs to have a UIForm in its ancestry. Suggestion: enclose the necessary components within " But I do have a wrapped around the CC. – jeff Aug 14 '15 at 13:41
  • I just noticed that if I put a debug marker on the setter for "#{qAndA.na_value}" it is never called when I click the checkbox. I'm assuming this is why the inputText is never disabled, but I don't know why the setter wouldn't be called. – jeff Aug 14 '15 at 16:17
  • Press F12 in browser and check console. Any clues? – BalusC Aug 14 '15 at 16:55
  • the getter is called for "#{qAndA.na_value}" during the iteration. But shouldn't the setter be called when I click the checkbox? – jeff Aug 14 '15 at 17:10
  • Does the form otherwise work fine without f:ajax? If so, this suggests that your ui:repeat construct is sensitive to that state saving bug. Try using h:dataTable instead. – BalusC Aug 14 '15 at 18:58
  • I believe it is working in the sense that when I submit the form, the checkbox values match how they are selected on the form. I also tried the dataTable with no luck. I then tried a single boolean checkbox on the checklist instance as opposed to on the array of answers that are part of the checklist instance and this works. I'll update my question to explain more. – jeff Aug 17 '15 at 20:38
  • Is listener the method really not invoked? How does the HTTP request payload look like? At least, `` attribute to update an element is named `update`, not `render`. – BalusC Aug 17 '15 at 20:47
  • in my q edit, yes the listener method is called only on the checkbox outside of the array butis not invoked on the array of checkboxes (what I am after). I was switching between jsf and primefaces to see what that affected and explains the update/render typo – jeff Aug 17 '15 at 20:54
  • How does the HTTP request payload look like? What does the HTTP response contain? Is the bean view or request scoped? By the way, composite components can better be used as last refactoring step once you got everything to work in a "single template" flavor (using at most includes to split long or repeated code into separate files). Now that composite adds some ambiguity to probable causes of your problem, essentially making your question a bit too broad. – BalusC Aug 17 '15 at 20:56
  • I did start with the includes but then some functionality couldn't be tested because I was getting error that "The class 'java.lang.String' does not have the property XXX" so I went to composites.... ok I just "got" your comment, Ill try the Single Template" – jeff Aug 17 '15 at 21:02
  • javax.faces.bean.ManagedBean and javax.faces.bean.ManagedBean I tried Named and Request too – jeff Aug 17 '15 at 21:04
  • When I moved everything into a single template and used update="@all" with p:ajax things worked, my inputbox gets grayed out and disabled. I'll edit my q to explain. – jeff Aug 17 '15 at 21:45
0

The fix was to use p:dataTable over the ui:repeat ...and use @form on the ajax update. I feel since BalusC tipped me off on the ui:repeat bug, I'll accept his answer as it was most helpful. But here is what worked

<composite:implementation>

  <p:dataTable id="table" value="#{cc.attrs.checklist.answer_attribute_list}" var="qnA">
    <p:column headerText="NA" width="50px;">
      <p:selectBooleanCheckbox value="#{qnA.not_applicable}">
        <p:ajax listener="#{checklistEdit.checkBoxListenerTest}" update="@form" />
      </p:selectBooleanCheckbox>

      <h:outputText value="X" rendered="#{qnA.not_applicable}" />

    </p:column>


    <p:column headerText="Quantity" width="75px;">
       <h:inputText value="#{qnA.quantity}" id="qty" rendered="#{qnA.not_applicable == false}" size="1" />
       <h:outputText value="#{qnA.quantity}" rendered="#{qnA.not_applicable == false}" />

       <h:outputText value="-" rendered="#{qnA.not_applicable}" />

    </p:column>

</p:dataTable>
jeff
  • 3,618
  • 9
  • 48
  • 101