ICEfaces
  1. ICEfaces
  2. ICE-6888

The value of an ACE checkbox button inside a tab is not remembered when changing tabs

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 2.0.0, EE-2.0.0.GA, 2.0.2
    • Fix Version/s: 3.0
    • Component/s: ACE-Components
    • Labels:
      None
    • Environment:
      Any
    • Assignee Priority:
      P1

      Description

      If we have a checkbox button inside a tab and we set it to true, and then we change tabs, when returning to the tab that contains the checkbox button, it will always be set to false, regardless of what is the default value in the bean. The test app used was /repo/qa/trunk/Regression-Icefaces2/Sparkle/Nightly/CheckboxBttn. Although the bean used in this app has the annotation @ViewScoped, the same problem occurred when changing it to @SessionScoped.

        Issue Links

          Activity

          Hide
          Judy Guglielmin added a comment -

          Looked at test page annTabset.xhtml and note that the checkboxButton is losing it's state, but exact same markup on PnlTabset.xhtml (which uses compat tabset) does not.

          Show
          Judy Guglielmin added a comment - Looked at test page annTabset.xhtml and note that the checkboxButton is losing it's state, but exact same markup on PnlTabset.xhtml (which uses compat tabset) does not.
          Hide
          Judy Guglielmin added a comment - - edited

          after the tab changes and comes back to tabPane with checkbox in it, in the encode method of CheckboxbuttonRenderer, the checkbox.getValue() method returns null so the value goes back to the default defined in the meta file (false).
          Looked further and the decode is being triggered twice after a tabset change and then a click on the button, so the value in the hiddenField is null (since the checkboxBttn isn't what is submitting the form). Something different with singleSubmit perhaps since this component was written?

          Show
          Judy Guglielmin added a comment - - edited after the tab changes and comes back to tabPane with checkbox in it, in the encode method of CheckboxbuttonRenderer, the checkbox.getValue() method returns null so the value goes back to the default defined in the meta file (false). Looked further and the decode is being triggered twice after a tabset change and then a click on the button, so the value in the hiddenField is null (since the checkboxBttn isn't what is submitting the form). Something different with singleSubmit perhaps since this component was written?
          Hide
          Judy Guglielmin added a comment - - edited

          change of tab from tab that contains the checkbox to the one that does, on submit does not have this component within the current tabPane, so the value is null. Must check for null values before updating in renderer. rev. 24656

          Show
          Judy Guglielmin added a comment - - edited change of tab from tab that contains the checkbox to the one that does, on submit does not have this component within the current tabPane, so the value is null. Must check for null values before updating in renderer. rev. 24656
          Hide
          Mark Collette added a comment -

          Can not reproduce. Tried with both trunk (scratchpad) and with maintenance branch.

          Show
          Mark Collette added a comment - Can not reproduce. Tried with both trunk (scratchpad) and with maintenance branch.
          Hide
          Mark Collette added a comment -

          Now understand that the issue is with ace:tabSet. Basically, when in server mode we only want to execute the current tabPane, and the headers of all the tabPanes. Right now we're executing everything. In client mode it's correct to execute everything.

          Particular care has to go into handling the transition from one tabPane to another. When immediate=false, the tabSet changes tabs in INVOKE_APPLICATION. As long as only the current tab executes in server mode, everything should be fine, as the events will have been queued before-hand, so we don't have to worry about the switched-to tab being processed, which it should not be. But, when immediate=true, then the tabSet changes tabs in APPLY_REQUEST_VALUES, which means we have two problems:

          1. VALIDATION and UPDATE_MODEL should definitely not happen on the newly selected tabPane, and most likely should not happen on the previously selected tabPane either, since immediate=true tends to be a mechanism to bypass validation.

          2. When recursing in for APPLY_REQUEST_VALUES, components outside of the tabSet will decode, but the question is should the current tabPane contents decode or not? Likely yes.

          If we use the compat PanelTabSet, which only operates in server mode, as an example, we see that it tracks the tab index changes, so that it can control which tab will have phases execute. It too uses immediate, which affects both when it's listener fires, and the flow of phases for its children. For decoding, the contents decode before the panelTabSet processes the tab change.

          There's code in PanelTabSet.applyPhase(FacesContext,PhaseId) that demonstrates the execution of the children and header facets appropriately.

          Show
          Mark Collette added a comment - Now understand that the issue is with ace:tabSet. Basically, when in server mode we only want to execute the current tabPane, and the headers of all the tabPanes. Right now we're executing everything. In client mode it's correct to execute everything. Particular care has to go into handling the transition from one tabPane to another. When immediate=false, the tabSet changes tabs in INVOKE_APPLICATION. As long as only the current tab executes in server mode, everything should be fine, as the events will have been queued before-hand, so we don't have to worry about the switched-to tab being processed, which it should not be. But, when immediate=true, then the tabSet changes tabs in APPLY_REQUEST_VALUES, which means we have two problems: 1. VALIDATION and UPDATE_MODEL should definitely not happen on the newly selected tabPane, and most likely should not happen on the previously selected tabPane either, since immediate=true tends to be a mechanism to bypass validation. 2. When recursing in for APPLY_REQUEST_VALUES, components outside of the tabSet will decode, but the question is should the current tabPane contents decode or not? Likely yes. If we use the compat PanelTabSet, which only operates in server mode, as an example, we see that it tracks the tab index changes, so that it can control which tab will have phases execute. It too uses immediate, which affects both when it's listener fires, and the flow of phases for its children. For decoding, the contents decode before the panelTabSet processes the tab change. There's code in PanelTabSet.applyPhase(FacesContext,PhaseId) that demonstrates the execution of the children and header facets appropriately.
          Hide
          Mark Collette added a comment -

          From the PanelTabSet docs:

          There's a tricky issue where were have to update our selectedIndex in the validation phase, and set its ValueBinding in the update model phase, but we have to keep the old selectedIndex so that we know which tab to be forwarding all the phases (including decode, validate, update model, and invoke application, but not render) invocations to.

          Show
          Mark Collette added a comment - From the PanelTabSet docs: There's a tricky issue where were have to update our selectedIndex in the validation phase, and set its ValueBinding in the update model phase, but we have to keep the old selectedIndex so that we know which tab to be forwarding all the phases (including decode, validate, update model, and invoke application, but not render) invocations to.
          Hide
          Ken Fyten added a comment -

          Possible that ICE-6870 is related, so Deryk will wait until this issue is resolved before revisiting that issue.

          Show
          Ken Fyten added a comment - Possible that ICE-6870 is related, so Deryk will wait until this issue is resolved before revisiting that issue.
          Hide
          Carmen Cristurean added a comment -

          Another issue which was found at icefaces3 rev. 26767:
          In IE8, the ace:checkboxBttn component does not render on page inside an ace:tabSet when first loading the test page. It will render after switching tabs.
          Test application: http://server.ice:8888/svn/repo/qa/trunk/Regression-Icefaces2/Sparkle/Nightly/CheckboxBttn
          Test web page: http://localhost:8080/CheckboxBttn/annTabset.jsf

          Show
          Carmen Cristurean added a comment - Another issue which was found at icefaces3 rev. 26767: In IE8, the ace:checkboxBttn component does not render on page inside an ace:tabSet when first loading the test page. It will render after switching tabs. Test application: http://server.ice:8888/svn/repo/qa/trunk/Regression-Icefaces2/Sparkle/Nightly/CheckboxBttn Test web page: http://localhost:8080/CheckboxBttn/annTabset.jsf
          Hide
          Mark Collette added a comment -

          I made changes to TabSet and TabPane to bring them in line with the PanelTabSet processing, where the execute phases' process* methods will recurse into the facets of all tabPane components when in server mode, and will only recurse into the contents of the tabPane that it was on before decoding.

          Unfortunately, that didn't fix the issue of in my test application of the inputText losing it's entered text. Basically, with immediate="true", cancelOnInvalid="true" the tabSet will skip phases after decode, so the submittedValue will be stuck in the inputText, which shouldn't be a problem, since it should still be there when we tab back, but it's not. From adding a phase listener, we can see that the submittedValue is there after the end of render response, but is gone before the beginning of the next lifecycle's decode. So the state is not being saved or restored.

          Show
          Mark Collette added a comment - I made changes to TabSet and TabPane to bring them in line with the PanelTabSet processing, where the execute phases' process* methods will recurse into the facets of all tabPane components when in server mode, and will only recurse into the contents of the tabPane that it was on before decoding. Unfortunately, that didn't fix the issue of in my test application of the inputText losing it's entered text. Basically, with immediate="true", cancelOnInvalid="true" the tabSet will skip phases after decode, so the submittedValue will be stuck in the inputText, which shouldn't be a problem, since it should still be there when we tab back, but it's not. From adding a phase listener, we can see that the submittedValue is there after the end of render response, but is gone before the beginning of the next lifecycle's decode. So the state is not being saved or restored.
          Hide
          Mark Collette added a comment -

          TabSet doesn't override UIComponent.visitTree, but I pasted it in to debug what's happening, and I can see that state saving and state restoring are happening. There's a lot of extraneous state restoration from dynamically added components, possibly due to specifics of the showcase application. Again, the submittedValue is in the inputText all the way through the first lifecycle, and is always missing in the second lifecycle where we tab back. One would expect it to be initially null, and then appear during state restoration.

          Show
          Mark Collette added a comment - TabSet doesn't override UIComponent.visitTree, but I pasted it in to debug what's happening, and I can see that state saving and state restoring are happening. There's a lot of extraneous state restoration from dynamically added components, possibly due to specifics of the showcase application. Again, the submittedValue is in the inputText all the way through the first lifecycle, and is always missing in the second lifecycle where we tab back. One would expect it to be initially null, and then appear during state restoration.
          Hide
          Mark Collette added a comment -

          Added an inputText outside of the tabSet, and while it appeared to retain its value, it actually lost it from state saving, and only regained it because it was re-submitted sice the component had remained rendered in the view. So, while it appears to the casual observer that tabSet behaves differently, this actually proves it's behaving the same.

          I'm going to do further testing to see if it's related to the form.

          Show
          Mark Collette added a comment - Added an inputText outside of the tabSet, and while it appeared to retain its value, it actually lost it from state saving, and only regained it because it was re-submitted sice the component had remained rendered in the view. So, while it appears to the casual observer that tabSet behaves differently, this actually proves it's behaving the same. I'm going to do further testing to see if it's related to the form.
          Hide
          Mark Collette added a comment -

          So, the phase execution is now corrected in TabSet, it just has to be committed, but this is the analysis of the continuance of the bad symptoms even after the code fix.

          I modified the basic application to use this view definition:

          <f:phaseListener
          type="org.icefaces.demo.basic.InputPhaseListener"/>
          <h:form id="myform">
          <h:inputText id="myinput" value="#

          {basic.input}

          "/>
          <h:commandButton id="mybutton" immediate="true" actionListener="#

          {basic.actionListener}

          "/>
          <h:panelGroup id="mygroup">
          <h:messages id="mymessages"/>
          </h:panelGroup>
          </h:form>

          and this bean code:

          public void actionListener(ActionEvent event)

          { System.out.println("Basic.actionListener " + FacesContext.getCurrentInstance().getCurrentPhaseId()); }

          public void setInput(String input)

          { System.out.println("Basic.setInput " + FacesContext.getCurrentInstance().getCurrentPhaseId() + " old: " + this.input + " new: " + input); this.input = input; }

          public String getInput()

          { System.out.println("Basic.getInput " + input); return input; }

          private String input;

          and this InputPhaseListener:

          public class InputPhaseListener implements PhaseListener {
          public void afterPhase(PhaseEvent phaseEvent)

          { FacesContext context = FacesContext.getCurrentInstance(); System.out.println("recursivelyPrintInputs() " + context.getCurrentPhaseId() + " AFTER"); recursivelyPrintInputs(context.getViewRoot()); }

          public void beforePhase(PhaseEvent phaseEvent)

          { FacesContext context = FacesContext.getCurrentInstance(); System.out.println("recursivelyPrintInputs() " + context.getCurrentPhaseId() + " BEFORE"); recursivelyPrintInputs(context.getViewRoot()); }

          public PhaseId getPhaseId()

          { return PhaseId.ANY_PHASE; }

          private static void recursivelyPrintInputs(UIComponent comp) {
          if (comp == null)

          { System.out.println("recursivelyPrintInputs() comp is null"); return; }

          if (comp instanceof javax.faces.component.UIInput)

          { printInputs((javax.faces.component.UIInput)comp); }

          for (Iterator<UIComponent> it = comp.getFacetsAndChildren(); it.hasNext()

          { UIComponent kid = it.next(); recursivelyPrintInputs(kid); }

          }

          private static void printInputs(javax.faces.component.UIInput comp)

          { System.out.println("printInputs() clientId: " + comp.getClientId()); System.out.println("printInputs() submittedValue: " + comp.getSubmittedValue()); System.out.println("printInputs() localValueSet: " + comp.isLocalValueSet()); System.out.println("printInputs() localValue: " + comp.getLocalValue()); System.out.println("printInputs() value: " + comp.getValue()); }

          }

          With all that, I tested the icefaces page and non-icefaces page, and the non-icefaces page with icefaces removed for a pure stock JSF scenario. In all of them, the phase listener showed that the inputText loses it's submittedValue in-between lifecycles. Auditing UIInput source shows that submittedValue is treated differently than all other fields, where is it specifically excluded from the StateHelper state saving mechanism.

          So the conclusion is that it's not tabSet's fault, when an input component loses it's submittedValue, when it does a renderResponse() after decode, which happens when immediate="true" and cancelOnInvalid="true".

          Show
          Mark Collette added a comment - So, the phase execution is now corrected in TabSet, it just has to be committed, but this is the analysis of the continuance of the bad symptoms even after the code fix. I modified the basic application to use this view definition: <f:phaseListener type="org.icefaces.demo.basic.InputPhaseListener"/> <h:form id="myform"> <h:inputText id="myinput" value="# {basic.input} "/> <h:commandButton id="mybutton" immediate="true" actionListener="# {basic.actionListener} "/> <h:panelGroup id="mygroup"> <h:messages id="mymessages"/> </h:panelGroup> </h:form> and this bean code: public void actionListener(ActionEvent event) { System.out.println("Basic.actionListener " + FacesContext.getCurrentInstance().getCurrentPhaseId()); } public void setInput(String input) { System.out.println("Basic.setInput " + FacesContext.getCurrentInstance().getCurrentPhaseId() + " old: " + this.input + " new: " + input); this.input = input; } public String getInput() { System.out.println("Basic.getInput " + input); return input; } private String input; and this InputPhaseListener: public class InputPhaseListener implements PhaseListener { public void afterPhase(PhaseEvent phaseEvent) { FacesContext context = FacesContext.getCurrentInstance(); System.out.println("recursivelyPrintInputs() " + context.getCurrentPhaseId() + " AFTER"); recursivelyPrintInputs(context.getViewRoot()); } public void beforePhase(PhaseEvent phaseEvent) { FacesContext context = FacesContext.getCurrentInstance(); System.out.println("recursivelyPrintInputs() " + context.getCurrentPhaseId() + " BEFORE"); recursivelyPrintInputs(context.getViewRoot()); } public PhaseId getPhaseId() { return PhaseId.ANY_PHASE; } private static void recursivelyPrintInputs(UIComponent comp) { if (comp == null) { System.out.println("recursivelyPrintInputs() comp is null"); return; } if (comp instanceof javax.faces.component.UIInput) { printInputs((javax.faces.component.UIInput)comp); } for (Iterator<UIComponent> it = comp.getFacetsAndChildren(); it.hasNext() { UIComponent kid = it.next(); recursivelyPrintInputs(kid); } } private static void printInputs(javax.faces.component.UIInput comp) { System.out.println("printInputs() clientId: " + comp.getClientId()); System.out.println("printInputs() submittedValue: " + comp.getSubmittedValue()); System.out.println("printInputs() localValueSet: " + comp.isLocalValueSet()); System.out.println("printInputs() localValue: " + comp.getLocalValue()); System.out.println("printInputs() value: " + comp.getValue()); } } With all that, I tested the icefaces page and non-icefaces page, and the non-icefaces page with icefaces removed for a pure stock JSF scenario. In all of them, the phase listener showed that the inputText loses it's submittedValue in-between lifecycles. Auditing UIInput source shows that submittedValue is treated differently than all other fields, where is it specifically excluded from the StateHelper state saving mechanism. So the conclusion is that it's not tabSet's fault, when an input component loses it's submittedValue, when it does a renderResponse() after decode, which happens when immediate="true" and cancelOnInvalid="true".
          Hide
          Mark Collette added a comment -

          Committed the phase execution changes.

          icefaces 3 trunk
          Subversion 27179

          Show
          Mark Collette added a comment - Committed the phase execution changes. icefaces 3 trunk Subversion 27179
          Hide
          Carmen Cristurean added a comment - - edited

          Fix verified in IE8, FF6, Chrome15, using Icefaces3 revision # 27279, and test from /repo/qa/trunk/Regression-Icefaces2/Sparkle/Nightly/CheckboxBttn.

          Show
          Carmen Cristurean added a comment - - edited Fix verified in IE8, FF6, Chrome15, using Icefaces3 revision # 27279, and test from /repo/qa/trunk/Regression-Icefaces2/Sparkle/Nightly/CheckboxBttn.

            People

            • Assignee:
              Mark Collette
              Reporter:
              Arturo Zambrano
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: