MyFaces Tomahawk
  1. MyFaces Tomahawk
  2. TOMAHAWK-521

Values from checkboxes not received on submit, when branches are collapsed.

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Critical Critical
    • Resolution: Fixed
    • Affects Version/s: 1.1.3
    • Fix Version/s: 1.1.5
    • Component/s: Tree2
    • Labels:
      None
    • Environment:
      NetBean IDE 5.0
      JSF 1.1 RI
      Sun Java System Application Server 9
      Java EE SDK 5 Windows

      Description

      I think this issue is best explained with an example, so here goes...

      I have a tree root. It is not expanded. It has a checkbox, which is not checked.
      [+][ ] root

      Then I expand the root, which reveals 2 children. The children too have checkboxes that are not checked.
      [-][ ] root

      • [ ] child1
      • [ ] child2

      I now check the root and the 2 children. If I were to submit now, everything would work as expected. Values from all 3 checkboxes would be received. But I won't...
      [-][x] root

      • [x] child1
      • [x] child2

      ... Instead I will collaps the tree again, leaving only the root visible.
      [+][x] root

      Finally, I submit the tree. In my backing bean I will then only receive the checkbox value of the root. The checkbox value of child1 and child2 is not submitted. Values from the 2 children do exist in the request scope, but for some reason the backing bean is not set with them.

      Here follows the code I'm using...

      JSF:
      <html:form id="createHost">
      <tomahawk:tree2 id="clientTree" value="#

      {ModuleBean.moduleHierarchy}

      "
      var="node" showRootNode="false" clientSideToggle="true">
      <core:facet name="root">
      <html:panelGroup>
      <tomahawk:htmlTag value="div">
      <html:outputText value="#

      {node.description}"/>
      <html:outputText value=" (#{node.childCount})"
      styleClass="childCount"
      rendered="#{!empty node.children}"/>
      </tomahawk:htmlTag>
      </html:panelGroup>
      </core:facet>
      <core:facet name="module">
      <html:panelGroup>
      <tomahawk:htmlTag value="div">
      <tomahawk:selectManyCheckbox id="moduleId" layout="spread"
      converter="#{HostBean.moduleIdTreeConverter}"
      value="#{HostBean.moduleId}">
      <tomahawk:treeCheckbox for="moduleId"
      itemValue="#{node.identifier}"
      itemLabel="#{node.description}

      "/>
      </tomahawk:selectManyCheckbox>
      <html:outputText value=" (#

      {node.childCount}

      )"
      styleClass="childCount"
      rendered="#

      {!empty node.children}

      "/>
      </tomahawk:htmlTag>
      </html:panelGroup>
      </core:facet>
      </tomahawk:tree2>
      <html:commandButton value="OK" action="#

      {HostBean.updateHostAction}

      "
      style="width: 25%;" styleClass="button"/>
      </html:form>

      HostBean:
      private void addModuleId(String moduleId) {
      if (moduleId != null && !moduleId.equals(""))
      if (!this.moduleId.contains(moduleId))
      this.moduleId.add(moduleId);
      }
      public List getModuleId() {
      return moduleId;
      }
      public void setModuleId(List moduleId) {
      for (int i = 0; i < moduleId.size(); i++)

      { String value = (String)moduleId.get(i); addModuleId(value); }

      }
      public Converter getModuleIdTreeConverter() {
      return new Converter() {
      public Object getAsObject(FacesContext context,
      UIComponent component, String newValue)
      throws ConverterException

      { addModuleId(newValue); return newValue; }

      public String getAsString(FacesContext context,
      UIComponent component, Object value)
      throws ConverterException

      { return (String)value; }

      };
      }
      public void loadHost(ActionEvent event) {
      /* Initializes data, when the user first clicks the edit button on a
      host.
      Loads both host data and modules. Modules are added using
      the addModuleId(String) method. */
      }
      public String updateHostAction() {
      CachingServiceLocator csl = CachingServiceLocator.getInstance();
      HostLocal hostLocal = csl.lookupHostBean();
      Integer[] moduleId = new Integer[this.moduleId.size()];
      for (int i = 0; i < moduleId.length; i++)

      { moduleId[i] = Integer.valueOf((String)this.moduleId.get(i)); }

      try

      { hostLocal.updateHost(id, languageId, currencyId, moduleId, name, mail, address); }

      catch (UpdateException ue) {}
      return "success";
      }

      ModuleBean:
      private void addChildModules(TreeNode node, TOList modules) {
      for (int i = 0; i < modules.getLimitedSize(); i++)

      { ModuleTO module = (ModuleTO)modules.get(i); TreeNode child = new TreeNodeBase("module", module.getName(), String.valueOf(module.getId()), false); node.getChildren().add(child); addChildModules(child, module.getChildModules()); }

      }
      public TreeNode getModuleHierarchy() {
      CachingServiceLocator csl = CachingServiceLocator.getInstance();
      ModuleLocal ml = csl.lookupModuleBean();
      TreeNode base = new TreeNodeBase("root", "ROOT", false);
      addChildModules(base, ml.loadModuleHierarchy());
      return base;
      }

      1. TOMAHAWK-521.patch
        2 kB
        Mathias Werlitz

        Activity

        Hide
        Norberto Eichstaedt added a comment -

        Hi, For me this is big issue since the end-user has not idea that I particular data will be saved. So, Would like to know if anyone there has a workaround for this issue?

        Show
        Norberto Eichstaedt added a comment - Hi, For me this is big issue since the end-user has not idea that I particular data will be saved. So, Would like to know if anyone there has a workaround for this issue?
        Hide
        Bjørn Stenfeldt added a comment -

        I had to find a work around too. Its a bit of work, but the best I could come up with. Here is a simplified example of what I did:

        JSF:
        <script language="JavaScript" type="text/javascript">
        function toggleTreeCheckbox(containerId, checkboxId) {
        var container = document.getElementById(containerId);
        var checkbox= document.getElementById(checkboxId);
        if (container != null && checkbox != null) {
        var checkboxValue = "

        {" + checkbox.value + "}

        ";
        if (container.value.indexOf(checkboxValue) == -1)

        { container.value = container.value + checkboxValue; }

        else

        { var splitValue = container.value.split(checkboxValue); container.value = splitValue[0] + splitValue[1]; }

        }
        }
        </script>
        <html:form id="myForm">
        <html:inputHidden id="idArray" value="#

        {BackingBean.idArray}

        "/>
        <tomahawk:tree2 id="tree" value="#

        {BackingBean.hierarchy}

        " var="node"
        showRootNode="false" preserveToggle="true" clientSideToggle="true">
        <core:facet name="root">
        <tomahawk:htmlTag value="div">
        <tomahawk:selectManyCheckbox id="id" layout="spread"
        converter="#

        {BackingBean.idTreeConverter}

        "
        value="#

        {BackingBean.id}

        "
        onchange="toggleTreeCheckbox('myForm:idArray', this.id);">
        <tomahawk:treeCheckbox for="id"
        itemValue="#

        {node.identifier}

        "
        itemLabel="#

        {node.description}

        "/>
        </tomahawk:selectManyCheckbox>
        </tomahawk:htmlTag>
        </core:facet>
        </tomahawk:tree2>

        Then when submitting, I would get the ids from the idArray string. Each individual id is surrounded by

        { and }

        . Its a mess, so I really hope they will fix this.

        Another solution might be to simple get the ids from the request scope and setting them in your backing bean yourself. I didnt have time to thing about this problem too much. Deadlines you know... :-/

        Show
        Bjørn Stenfeldt added a comment - I had to find a work around too. Its a bit of work, but the best I could come up with. Here is a simplified example of what I did: JSF: <script language="JavaScript" type="text/javascript"> function toggleTreeCheckbox(containerId, checkboxId) { var container = document.getElementById(containerId); var checkbox= document.getElementById(checkboxId); if (container != null && checkbox != null) { var checkboxValue = " {" + checkbox.value + "} "; if (container.value.indexOf(checkboxValue) == -1) { container.value = container.value + checkboxValue; } else { var splitValue = container.value.split(checkboxValue); container.value = splitValue[0] + splitValue[1]; } } } </script> <html:form id="myForm"> <html:inputHidden id="idArray" value="# {BackingBean.idArray} "/> <tomahawk:tree2 id="tree" value="# {BackingBean.hierarchy} " var="node" showRootNode="false" preserveToggle="true" clientSideToggle="true"> <core:facet name="root"> <tomahawk:htmlTag value="div"> <tomahawk:selectManyCheckbox id="id" layout="spread" converter="# {BackingBean.idTreeConverter} " value="# {BackingBean.id} " onchange="toggleTreeCheckbox('myForm:idArray', this.id);"> <tomahawk:treeCheckbox for="id" itemValue="# {node.identifier} " itemLabel="# {node.description} "/> </tomahawk:selectManyCheckbox> </tomahawk:htmlTag> </core:facet> </tomahawk:tree2> Then when submitting, I would get the ids from the idArray string. Each individual id is surrounded by { and } . Its a mess, so I really hope they will fix this. Another solution might be to simple get the ids from the request scope and setting them in your backing bean yourself. I didnt have time to thing about this problem too much. Deadlines you know... :-/
        Hide
        Olga Penina added a comment -

        It's not true that the checkboxes are not being submitted when the node is collapsed - they are.

        The problem is that during the processDecodes() phase the TreeWalker ignores the collapsed nodes.

        Like some of you we've discovered this a bit too late. To make the fix as painless as possible, I just made a new tag ( Tree3 ! ) and a matching UI component that extends
        org.apache.myfaces.custom.tree2.HtmlTree and the only method I had to override was processNodes():

        <code>
        protected void processNodes(FacesContext...)
        {
        UIComponent facet = null;
        TreeWalker walker = getDataModel().getTreeWalker();
        walker.reset();
        walker.setTree(this);

        // This will make it see all the collapsed nodes:
        walker.setCheckState(false);
        // the rest is the same...
        </code>

        I am sure that there is a way to configure the TreeWalker from the DataModel also.

        "Problems cannot be solved at the same level of awareness that created
        them."

        Show
        Olga Penina added a comment - It's not true that the checkboxes are not being submitted when the node is collapsed - they are. The problem is that during the processDecodes() phase the TreeWalker ignores the collapsed nodes. Like some of you we've discovered this a bit too late. To make the fix as painless as possible, I just made a new tag ( Tree3 ! ) and a matching UI component that extends org.apache.myfaces.custom.tree2.HtmlTree and the only method I had to override was processNodes(): <code> protected void processNodes(FacesContext...) { UIComponent facet = null; TreeWalker walker = getDataModel().getTreeWalker(); walker.reset(); walker.setTree(this); // This will make it see all the collapsed nodes: walker.setCheckState(false); // the rest is the same... </code> I am sure that there is a way to configure the TreeWalker from the DataModel also. "Problems cannot be solved at the same level of awareness that created them."
        Hide
        Mathias Werlitz added a comment -

        I can confirm this bug. In client side mode all nodes should run through the full processing cycle, in server side mode the code is okay.
        I provided a patch for this issue.

        One thing I have to note:
        The user may enter some invalid data in a collapsed branch of the tree ... as the validation takes place an error message is added but the user may not see this local message.

        Show
        Mathias Werlitz added a comment - I can confirm this bug. In client side mode all nodes should run through the full processing cycle, in server side mode the code is okay. I provided a patch for this issue. One thing I have to note: The user may enter some invalid data in a collapsed branch of the tree ... as the validation takes place an error message is added but the user may not see this local message.
        Hide
        Grant Smith added a comment -

        Patch applied. Thanks for finding this !

        Show
        Grant Smith added a comment - Patch applied. Thanks for finding this !
        Hide
        Grant Smith added a comment -

        Forgot to state the fix version...

        Show
        Grant Smith added a comment - Forgot to state the fix version...
        Hide
        Grant Smith added a comment -

        Fix version...

        Show
        Grant Smith added a comment - Fix version...

          People

          • Assignee:
            Grant Smith
            Reporter:
            Bjørn Stenfeldt
          • Votes:
            1 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development