Uploaded image for project: 'ZK'
  1. ZK
  2. ZK-2534

Listbox allows selection with checkable="false"

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Normal
    • Resolution: Fixed
    • Affects Version/s: 7.0.3
    • Fix Version/s: 8.0.0
    • Component/s: ZK Client Engine
    • Security Level: Jean
    • Labels:
    • Environment:

      All browsers, Tomcat 7

    • gh.sprint.customfield.default.name:
      ZK 8.0.1

      Description

      Steps to Reproduce : Case 1

      Demo here:
      http://zkfiddle.org/sample/3c3tr14/1-listbox-selectall-checkable-false

      By ticking the checkbox in the listbox header, all list items are selected, even those with checkable="false".


      Steps to Reproduce : Case 2

      1. run listbox-checkmark.zul
      2. click listhead to select all
      3. scroll down to last item

      Actual Result

      Some items without checkmark are selected.

      Expected

      only items with checkmark are selected.

      Debug Information

      Those items are not fully rendered because of CE ROD, so their isCheckable() returns true before we scroll to render them.

        Issue Links

          Activity

          Hide
          hawk hawk added a comment -

          Workaround

          Modified Function Specification:
          1. "Select All" (click a Listheader) only selects those checkable Listitems.
          2. If one of checkable Listitems is unchecked, the checkbox on the Listheader is also unchecked.

              <script defer="true"><![CDATA[
                  zul.sel.SelectWidget.prototype.selectAll = function (notify, evt){
          			for (var it = this.getBodyWidgetIterator(), w; (w = it.next());)
          				if (!w.isDisabled() && w.isCheckable()){
          					this._changeSelect(w, true);
          				}
          			if (notify && evt !== true)
          				this.fireOnSelect(this.getSelectedItem(), evt);
                  }      
                  
                  zul.sel.SelectWidget.prototype._isAllSelected = function () {
          			//B70-ZK-1953: if selectedItems is empty return false.
          			if (!this._selItems.length)
          				return false;
          			var isGroupSelect = this.groupSelect;
          			for (var it = this.getBodyWidgetIterator({skipHidden:true}), w; (w = it.next());) {
          			    //ignore non-checkable
          			    if (!w.isCheckable()){
          			    	continue;
          			    }
          				//Bug ZK-1998: skip listgroup and listgroupfoot widget if groupSelect is false
          				if ((_isListgroup(w) || _isListgroupfoot(w)) && !isGroupSelect)
          					continue;
          				if (!w.isDisabled() && !w.isSelected())
          					return false;
          			}
          			return true;
          		}
                  
              	function _isListgroup(w) {
              		return zk.isLoaded('zkex.sel') && w.$instanceof(zkex.sel.Listgroup);
              	}
              	function _isListgroupfoot(w) {
              		return zk.isLoaded('zkex.sel') && w.$instanceof(zkex.sel.Listgroupfoot);
              	}
              ]]></script>
          
          Show
          hawk hawk added a comment - Workaround Modified Function Specification: 1. "Select All" (click a Listheader) only selects those checkable Listitems. 2. If one of checkable Listitems is unchecked, the checkbox on the Listheader is also unchecked. <script defer= " true " ><![CDATA[ zul.sel.SelectWidget.prototype.selectAll = function (notify, evt){ for ( var it = this .getBodyWidgetIterator(), w; (w = it.next());) if (!w.isDisabled() && w.isCheckable()){ this ._changeSelect(w, true ); } if (notify && evt !== true ) this .fireOnSelect( this .getSelectedItem(), evt); } zul.sel.SelectWidget.prototype._isAllSelected = function () { //B70-ZK-1953: if selectedItems is empty return false . if (! this ._selItems.length) return false ; var isGroupSelect = this .groupSelect; for ( var it = this .getBodyWidgetIterator({skipHidden: true }), w; (w = it.next());) { //ignore non-checkable if (!w.isCheckable()){ continue ; } //Bug ZK-1998: skip listgroup and listgroupfoot widget if groupSelect is false if ((_isListgroup(w) || _isListgroupfoot(w)) && !isGroupSelect) continue ; if (!w.isDisabled() && !w.isSelected()) return false ; } return true ; } function _isListgroup(w) { return zk.isLoaded('zkex.sel') && w.$ instanceof (zkex.sel.Listgroup); } function _isListgroupfoot(w) { return zk.isLoaded('zkex.sel') && w.$ instanceof (zkex.sel.Listgroupfoot); } ]]></script>
          Hide
          jumperchen jumperchen added a comment -

          Bug fixed since 11/25/2014

          Show
          jumperchen jumperchen added a comment - Bug fixed since 11/25/2014
          Hide
          hawk hawk added a comment -

          Reopen for failure in case 2. I have tested on 7.0.4.FL.20141201-Eval

          Show
          hawk hawk added a comment - Reopen for failure in case 2. I have tested on 7.0.4.FL.20141201-Eval
          Hide
          hanhsu hanhsu added a comment - - edited

          Proposed Workaround:

          Zul:

          <zk>
          	<zscript><![CDATA[
          List names = new ArrayList();
          	names.add("Jasmin");
          	names.add("David");
          	names.add("Richard");
          	names.add("Jean");
          	names.add("Norman");
          	names.add("Thomas");
          	names.add("Leonard");
          	names.add("Janine");
          	names.add("Daniel");
          	names.add("Michael");
          	names.add("Julia");
          	names.add("Vitali");
          	names.add("Katharina");
          	names.add("Marie");
          	names.add("Jenny");
          	names.add("Reinhard");
          	names.add("Christoph");
          	names.add("Heiko");
          	names.add("Ludwig");
          	names.add("Nico");
          	names.add("Rolf");
          	ListModelList model = new ListModelList(names);
          ]]>
          	</zscript>
          	<script><![CDATA[
          	zk.afterLoad(function () {
          		function _isListgroup(w) {
          			return zk.isLoaded('zkex.sel') && w.$instanceof(zkex.sel.Listgroup);
          		}
          		function _isListgroupfoot(w) {
          			return zk.isLoaded('zkex.sel') && w.$instanceof(zkex.sel.Listgroupfoot);
          		}
          		zk.override(zul.sel.SelectWidget.prototype, '_isAllSelected', function () {
          			if (!this._selItems.length)
          				return false;
          			var isGroupSelect = this.groupSelect;
          			for (var it = this.getBodyWidgetIterator({skipHidden:true}), w; (w = it.next());) {
          				if (!w.isCheckable() || !w._loaded) // 2534 workaround: skip unloaded items
          			    	continue;
          				if ((_isListgroup(w) || _isListgroupfoot(w)) && !isGroupSelect)
          					continue;
          				if (!w.isDisabled() && !w.isSelected())
          					return false;
          			}
          			return true;
          		});
          		
          		zk.override(zul.sel.SelectWidget.prototype, 'selectAll', function (notify, evt) {
          			for (var it = this.getBodyWidgetIterator(), w; (w = it.next());)
          				if (!w.isDisabled() && w.isCheckable())
          					this._changeSelect(w, true);
          			if (notify && evt !== true)
          				this.fireOnSelect(this.getSelectedItem(), evt);
          		});
          		
          		
          	});
          ]]></script>
          	<window apply="foo.Composer">
          		<custom-attributes org.zkoss.zul.listbox.rod="false" />
          		<listbox id="lb" model="${model}" rows="5" multiple="true"
          			onCheckSelectAll="" checkmark="true">
          			<listhead>
          				<listheader hflex="min" />
          				<listheader label="Name" />
          			</listhead>
          			<template name="model">
          				<listitem checkable="${forEachStatus.index % 2 eq 0}">
          					<listcell />
          					<listcell label="${each}" />
          				</listitem>
          			</template>
          		</listbox>
          	</window>
          </zk>
          
          

          Composer:

          package foo;
          
          import java.util.ArrayList;
          import java.util.Collection;
          
          import org.zkoss.zk.ui.event.CheckEvent;
          import org.zkoss.zk.ui.select.SelectorComposer;
          import org.zkoss.zk.ui.select.annotation.Listen;
          import org.zkoss.zk.ui.select.annotation.Wire;
          import org.zkoss.zul.ListModel;
          import org.zkoss.zul.Listbox;
          import org.zkoss.zul.Listhead;
          import org.zkoss.zul.Window;
          import org.zkoss.zul.ext.Selectable;
          
          public class Composer extends SelectorComposer<Window> {
          
          	@Wire
          	Listbox lb;
          
          	@Listen("onCheckSelectAll=#lb")
          	public void doCheckSelectAll(CheckEvent evt) {
          		ListModel model = lb.getModel();
          		if (evt.isChecked()) {
          			int sz = model.getSize();
          			Collection clt = new ArrayList();
          			for (int i = 0; i < sz; i++) {
          				if (i % 2 == 0) {
          					Object obj = model.getElementAt(i);
          					clt.add(obj);
          				}
          			}
          			((Selectable) model).setSelection(clt);
          		} else {
          			((Selectable) model).clearSelection();
          		}
          	}
          }
          
          

          note: the items put in setSelection() in composer have to sync with the "checkable" items in zul

          Show
          hanhsu hanhsu added a comment - - edited Proposed Workaround: Zul: <zk> <zscript><![CDATA[ List names = new ArrayList(); names.add( "Jasmin" ); names.add( "David" ); names.add( "Richard" ); names.add( "Jean" ); names.add( "Norman" ); names.add( "Thomas" ); names.add( "Leonard" ); names.add( "Janine" ); names.add( "Daniel" ); names.add( "Michael" ); names.add( "Julia" ); names.add( "Vitali" ); names.add( "Katharina" ); names.add( "Marie" ); names.add( "Jenny" ); names.add( "Reinhard" ); names.add( "Christoph" ); names.add( "Heiko" ); names.add( "Ludwig" ); names.add( "Nico" ); names.add( "Rolf" ); ListModelList model = new ListModelList(names); ]]> </zscript> <script><![CDATA[ zk.afterLoad(function () { function _isListgroup(w) { return zk.isLoaded('zkex.sel') && w.$ instanceof (zkex.sel.Listgroup); } function _isListgroupfoot(w) { return zk.isLoaded('zkex.sel') && w.$ instanceof (zkex.sel.Listgroupfoot); } zk.override(zul.sel.SelectWidget.prototype, '_isAllSelected', function () { if (! this ._selItems.length) return false ; var isGroupSelect = this .groupSelect; for ( var it = this .getBodyWidgetIterator({skipHidden: true }), w; (w = it.next());) { if (!w.isCheckable() || !w._loaded) // 2534 workaround: skip unloaded items continue ; if ((_isListgroup(w) || _isListgroupfoot(w)) && !isGroupSelect) continue ; if (!w.isDisabled() && !w.isSelected()) return false ; } return true ; }); zk.override(zul.sel.SelectWidget.prototype, 'selectAll', function (notify, evt) { for ( var it = this .getBodyWidgetIterator(), w; (w = it.next());) if (!w.isDisabled() && w.isCheckable()) this ._changeSelect(w, true ); if (notify && evt !== true ) this .fireOnSelect( this .getSelectedItem(), evt); }); }); ]]></script> <window apply= "foo.Composer" > <custom-attributes org.zkoss.zul.listbox.rod= " false " /> <listbox id= "lb" model= "${model}" rows= "5" multiple= " true " onCheckSelectAll= "" checkmark=" true "> <listhead> <listheader hflex= "min" /> <listheader label= "Name" /> </listhead> <template name= "model" > <listitem checkable= "${forEachStatus.index % 2 eq 0}" > <listcell /> <listcell label= "${each}" /> </listitem> </template> </listbox> </window> </zk> Composer: package foo; import java.util.ArrayList; import java.util.Collection; import org.zkoss.zk.ui.event.CheckEvent; import org.zkoss.zk.ui.select.SelectorComposer; import org.zkoss.zk.ui.select.annotation.Listen; import org.zkoss.zk.ui.select.annotation.Wire; import org.zkoss.zul.ListModel; import org.zkoss.zul.Listbox; import org.zkoss.zul.Listhead; import org.zkoss.zul.Window; import org.zkoss.zul.ext.Selectable; public class Composer extends SelectorComposer<Window> { @Wire Listbox lb; @Listen( "onCheckSelectAll=#lb" ) public void doCheckSelectAll(CheckEvent evt) { ListModel model = lb.getModel(); if (evt.isChecked()) { int sz = model.getSize(); Collection clt = new ArrayList(); for ( int i = 0; i < sz; i++) { if (i % 2 == 0) { Object obj = model.getElementAt(i); clt.add(obj); } } ((Selectable) model).setSelection(clt); } else { ((Selectable) model).clearSelection(); } } } note: the items put in setSelection() in composer have to sync with the "checkable" items in zul
          Hide
          CJahn CJahn added a comment -

          Any ETA for the final bugfix?

          Show
          CJahn CJahn added a comment - Any ETA for the final bugfix?
          Hide
          jumperchen jumperchen added a comment -

          Bug fixed since 8/5/2015

          Show
          jumperchen jumperchen added a comment - Bug fixed since 8/5/2015

            People

            • Assignee:
              jumperchen jumperchen
              Reporter:
              CJahn CJahn
            • Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Time Tracking

                Estimated:
                Original Estimate - 0 minutes
                0m
                Remaining:
                Remaining Estimate - 0 minutes
                0m
                Logged:
                Time Spent - 2 days, 4 hours
                2d 4h

                  Agile