Problem Description:
Combobox has null value initially. Pressing down arrow key fires an onSelect event, but not onChange event. While servicing the onSelect event, the server-side will perform constraint validation on the current combobox's value. Since the value is null, it is coerced to an empty string, and violates the "no empty" constraint.
Steps to reproduce:
1. Set focus on the first combobox's text input box
2. Press "down arrow" key to change its selection
-> error message shows the violation of "no empty" constraint
3. Press "tab" to set focus on the second combobox
-> model, dependent on the first combobox's value, is not set
<zk> <zscript><![CDATA[ ListModelList items = new ListModelList(); items.add("d1"); items.add("d2"); items.add("d3"); ListModelList otherItems = null; ListModelList sub1 = new ListModelList(); sub1.add("d1s1"); sub1.add("d1s2"); sub1.add("d1s3"); ListModelList sub2 = new ListModelList(); sub2.add("d2s1"); sub2.add("d2s2"); sub2.add("d2s3"); ListModelList sub3 = new ListModelList(); sub3.add("d3s1"); sub3.add("d3s2"); sub3.add("d3s3"); void doSelect(String value) { System.out.println("doSelect: " + value); if ("d1".equals(value)) secondCombo.setModel(sub1); else if ("d2".equals(value)) secondCombo.setModel(sub2); else secondCombo.setModel(sub3); } ]]></zscript> <combobox id="firstCombo" model="${items}" onSelect="doSelect(self.value)" constraint="no empty"/> <combobox id="secondCombo" constraint="no empty"/> </zk>
Workaround:
Apply the following script
zk.afterLoad('zul.inp', function() { var _Combobox = {}; zk.override(zul.inp.Combobox.prototype, _Combobox, { _updnSel: function (evt, bUp) { var inp = this.getInputNode(), val = inp.value, sel, looseSel; // ZK-2200: the empty combo item should work if (val || this._sel) { val = val.toLowerCase(); var beg = this._sel, last = this._next(null, bUp); if (!beg || beg.parent != this){ beg = this._next(null, !bUp); } if (!beg) { evt.stop(); return; //ignore it } //Note: we always assume strict when handling up/dn for (var item = beg;;) { if (!item.isDisabled() && item.isVisible()) { var label = item.getLabel().toLowerCase(); if (val == label) { sel = item; break; } else if (!looseSel && label.startsWith(val)) { looseSel = item; break; } } var nextitem = this._next(item, bUp); if( item == nextitem ) break; //prevent infinite loop if ((item = nextitem) == beg) break; } if (!sel) sel = looseSel; if (sel) { //exact match var ofs = zk(inp).getSelectionRange(); if (ofs[0] == 0 && ofs[1] == val.length){ //full selected sel = this._next(sel, bUp); //next } } else{ sel = this._next(null, !bUp); } } else{ sel = this._next(null, true); } if (sel) zk(sel).scrollIntoView(this.$n('pp')); if (!val && sel) this.fire('onChange', { value: sel._label }); this._select(sel, {sendOnSelect:true}); evt.stop(); } }); });