0

MVVM combobox with model: isn't there an easy way to deal with the value???

asked 2016-01-02 22:21:56 +0800

KlausWr gravatar image KlausWr
37 5

updated 2016-01-02 22:23:51 +0800

I am really a fan of zk. But some of the concepts(?) still drive me crazy!

It took me now 2 hours to figure out that there is no easy way to use a combobox (in MVVM with model binding) and deal with the comboitem's "value" as the comobox' selected code instantly.

While this simple sample (works):

java: 
   public List<String> getList() {...} // "A", "B", "C"
   public String getSelectedItem() {...}
   public void setSelectedItem(String item) {...}

zul: <combobox model="@load(vm.list)" selectedItem="@bind(vm.selectedItem)" />
       <template name="model">
          <comboitem label="@load(each)" />
       </template>
      </combobox>

You cannot just add a "value" to the comboitem and expect this value to be returned when selected:

java: 
   public List<NameValuePair> getList() {...} // "A"->"1", "B"->"2", "C"->"3"
   public String getSelectedItem() {...}
   public void setSelectedItem(String item) {...}

zul: <combobox model="@load(vm.list)" selectedItem="@bind(vm.selectedItem)" />
       <template name="model">
          <comboitem label="@load(each.name)" value="@load(each.value)" />
       </template>
      </combobox>

Well, the setter setSelectedItem gets called (!) but the value is NOT the value of the selected item (e.g. "2") BUT the mangeled instance name of the selected NameValuePair or Comboitem instance or ??? (huh??).

After googeling tons of questions and answers I now know that the selectedItem always deals with an instance of Comboitem and you have to use getValue() on this instance to get the real value. Same for setting the initial selection. Somehow same. Never as easy as a string compare!

And there are hundreds of other tips to get it done (custom item renderer, model class implementing Comparable, ...

Hey, isnt't it the most obvious use of a combobox to present a human readable label and let select a machine readable unique code and then use this code again to mark the selected item in the combobox?

I know that combobox obviously works as designed.

But then - sorry - the design is overengineered and crappy.

Still a fan ;-( Klaus

delete flag offensive retag edit

3 Answers

Sort by ยป oldest newest most voted
0
link publish delete flag offensive edit
0

answered 2016-01-04 08:59:41 +0800

KlausWr gravatar image KlausWr
37 5

Thx, Senthil.

Your suggestion is what I mentioned as one of the hundreds of other tips to get it done. It works, but is not straight forward.

My personal favorite is what @gekkio posted in Combobox doesn't have key-value pairs working like html <SELECT> tag - Please Help..

The "Scala" implementation of a custom combobox class (extending combobox) introducing a "selectedValue" attribute is exatcly what I am missing from the standard implementation!

I understand, that the "value" attribute of the standard combobox cannot serve this need as it should reflect the "textbox" content of the (non-readable-) combobox. So adding an intuitive new attribute selectedValue (next to selectedIndex and selectedItem) is a brillant idea.

I translated gekkio's "Scala" proposal into Java like this:

package test;

import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.SelectEvent;
import org.zkoss.zul.Combobox;
import org.zkoss.zul.Comboitem;

public class CustomCombobox extends Combobox {
    private static final long serialVersionUID = 1L;
    private String _selectedValue = null;

    public String getSelectedValue() {
        return _selectedValue;
    }

    public void setSelectedValue(String selectedValue) {
        _selectedValue = selectedValue;
    }

    public void onAfterRender() {
        setReadonly(true);
        int selectedIndex = 0;

        for (Comboitem item : getItems()) {
            if (null != _selectedValue && _selectedValue.equals(item.getValue())) {
                setSelectedIndex(selectedIndex);
                break;
            }
            selectedIndex++;
        }
    }

    public void onSelect(SelectEvent<Comboitem, Event> event) {
        Comboitem selectedItem = event.getReference();
        if (selectedItem != null) {
            setSelectedValue((String) selectedItem.getValue());
        }
    }

}

As gekkio did not regard the MVVM binding there was a need to change the initial event handler from "onCreate" to "onAfter Render". And in order to allow two-way data binding (@load and @save) an extension to the standard component's language-addon is neccessary in you e.g. WEB-INF/zk/lang-addon.xml like:

...
    <component>
        <component-name>combobox</component-name>
        <extends>combobox</extends>
        <annotation>
            <annotation-name>ZKBIND</annotation-name>
            <property-name>selectedValue</property-name>
            <attribute>
                <attribute-name>LOAD_EVENT</attribute-name>
                <attribute-value>onAfterRender</attribute-value>
            </attribute>
            <attribute>
                <attribute-name>SAVE_EVENT</attribute-name>
                <attribute-value>onChange</attribute-value>
            </attribute>
            <attribute>
                <attribute-name>ACCESS</attribute-name>
                <attribute-value>both</attribute-value>
            </attribute>
        </annotation>
    </component>
...

Then you can use the customCombobox in a zul:

        <combobox model="@load(vm.nameValuePair)" 
            readonly="true" 
            use="test.CustomCombobox"
            selectedValue="@bind(vm.value)">

            <template name="model" var="entry">
                <comboitem 
                    label="@load(entry.key)" 
                    value="@load(entry.value)" />
            </template>
        </combobox>

The "use=" tag is one option. One can also shift the extension of combobox entirely into the language-addon.

This solution (which I heavily wish to be implemented in the standard combobox) works quite fine with one important bug which I describe in the next posting.

link publish delete flag offensive edit
0

answered 2016-01-04 09:05:57 +0800

KlausWr gravatar image KlausWr
37 5

The above solution (which I heavily wish to be implemented in the standard combobox) works quite fine with one important bug:

the evaluation of selectedValue for @save (or @bind) is always one UI cycle too late ????.

If I select another entry in the combobox the setter in the view model always gets the previous value over and over. It seems as if the new selected value is set after the view model is satisfied.

PLEASE ASSIST!!!

link publish delete flag offensive edit
Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!

[hide preview]

Question tools

Follow
1 follower

RSS

Stats

Asked: 2016-01-02 22:21:56 +0800

Seen: 93 times

Last updated: Jan 04 '16

Support Options
  • Email Support
  • Training
  • Consulting
  • Outsourcing
Learn More