Thursday, February 23, 2012

GXT - ComoboBox with Multi select feature

Hi There! :-)

Its been a while since I am working on GWT(Google Web Toolkit) and its wrapper framework like ext-GWT (which is called as GXT) and have come across lots of new widgets and containers.

One recent feature request, I had to work on recently, which caught my attention was Mutli-Select ComboBox.

What does it mean ?
Its a ComboBox (a widget which will allow user to edit - as well as select values from multiple options it provides)

How its different from normal ComboBox?
Multi-ComboBox allows you to select multiple values from options (unlike normal combo which allows to select only one value at a time)

Now as I went on to analyse the implementation of this widget, I remembered a component from GXT called CheckBoxListView. Which is a list view comprising of CheckBox components. As I found this component perfect to select multiple values, I went on to integrate it with TriggerField.

I thought of creating a Dialog widget which will act as ComboBox "option provider" and reacts to the user click event on  TriggerField  to either hide OR show the options.

To create such kind of Dialog which looks like plain floating window, I set its properties as follows


checkBoxListHolder = new Dialog();
checkBoxListHolder.setClosable(false);
checkBoxListHolder.setHeaderVisible(false);
checkBoxListHolder.setFooter(false);
checkBoxListHolder.setFrame(false);
checkBoxListHolder.setResizable(false);
checkBoxListHolder.setAutoHide(false);
checkBoxListHolder.getButtonBar().setVisible(false);
checkBoxListHolder.setLayout(new FillLayout());



To integrate above Dialog with  TriggerField , make the Dialog respond to the click event on the  TriggerField overriding the following method was heplful,



        @Override
protected void onTriggerClick(ComponentEvent ce) {
super.onTriggerClick(ce);
checkBoxListHolder.setSize(getWidth(), 200);
listView.setWidth(getWidth());
checkBoxListHolder.setPosition(getAbsoluteLeft(), 
getAbsoluteTop() + getHeight());
if(checkBoxListHolder.isVisible()) {
checkBoxListHolder.hide();
}
else {
checkBoxListHolder.show();
}
}




Finally, add the CheckBoxListView component to the Dialog and now the Multi-Select ComboBox is ready to use!
             
                listView =  new CheckBoxListView();

       checkBoxListHolder.add(listView);

And it looks like...




Here is the complete code listing..


package com.ui.test.client;


import java.util.List;


import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.WindowEvent;
import com.extjs.gxt.ui.client.event.WindowListener;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.widget.CheckBoxListView;
import com.extjs.gxt.ui.client.widget.Dialog;
import com.extjs.gxt.ui.client.widget.form.TriggerField;
import com.extjs.gxt.ui.client.widget.layout.FillLayout;
import com.google.gwt.user.client.Element;


public class MultiSelectComboBox<D extends ModelData> extends TriggerField<String> {


private Dialog checkBoxListHolder;
private CheckBoxListView<D> listView;
private ListStore<D> store;


private String delimiter = ",";
private boolean readOnly;




public MultiSelectComboBox() {
store = new ListStore<D>();
listView = new CheckBoxListView<D>();
}







@Override
protected void onTriggerClick(ComponentEvent ce) {
super.onTriggerClick(ce);
checkBoxListHolder.setSize(getWidth(), 200);
listView.setWidth(getWidth());
checkBoxListHolder.setPosition(getAbsoluteLeft(), 
getAbsoluteTop() + getHeight());
if(checkBoxListHolder.isVisible()) {
checkBoxListHolder.hide();
}
else {
checkBoxListHolder.show();
}
}








@Override
protected void onRender(Element target, int index) {
super.onRender(target, index);

checkBoxListHolder = new Dialog();
checkBoxListHolder.setClosable(false);
checkBoxListHolder.setHeaderVisible(false);
checkBoxListHolder.setFooter(false);
checkBoxListHolder.setFrame(false);
checkBoxListHolder.setResizable(false);
checkBoxListHolder.setAutoHide(false);
checkBoxListHolder.getButtonBar().setVisible(false);
checkBoxListHolder.setLayout(new FillLayout());
checkBoxListHolder.add(listView);
listView.setStore(store);


checkBoxListHolder.addWindowListener(new WindowListener(){


@Override
public void windowHide(WindowEvent we) {
setValue(parseCheckedValues(listView));
}


});


}








private String parseCheckedValues(CheckBoxListView<D> checkBoxView) {
StringBuffer buf = new StringBuffer();
if(checkBoxView != null) {
List<D> selected = checkBoxView.getChecked();
int index = 1, len = selected.size();
for(D c : selected) {
buf.append(c.get(listView.getDisplayProperty()));
if(index < len) {
buf.append(delimiter);
}
index++;
}
}
return buf.toString();
}


public CheckBoxListView<D> getListView() {
return listView;
}


public void setListView(CheckBoxListView<D> listView) {
this.listView = listView;
}


public ListStore<D> getStore() {
return store;
}


public void setStore(ListStore<D> store) {
this.store = store;
}


public String getDelimiter() {
return delimiter;
}


public void setDelimiter(String delimiter) {
this.delimiter = delimiter;
}


public boolean isReadOnly() {
return readOnly;
}


public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}




}


And it can be used from any part of your application like below example.. 

     public void onModuleLoad() {
MultiSelectComboBox<ModelData> combo1 = new MultiSelectComboBox<ModelData>();
combo1.getListView().setDisplayProperty("label");
combo1.getStore().add(getModelData());
combo1.setWidth(200);
RootPanel.get().add(combo1);
MultiSelectComboBox<ModelData> combo2 = new MultiSelectComboBox<ModelData>();
combo2.getListView().setDisplayProperty("label");
combo2.getStore().add(getModelData());
combo2.setWidth(300);
 
RootPanel.get().add(new Label("Multi-Select-Combo : "));
RootPanel.get().add(combo2);
}  
 
 
private List<ModelData> getModelData() {
List<ModelData> list = new ArrayList<ModelData>();
for(int i=0; i< 10; i++) {
ModelData m = new BaseModel();
m.set("label", "Label " + i);
list.add(m);
}
return list;
}
    



Share your comments and thoughts!... 

5 comments:

  1. this is a nice way to do this.. But i found what i felt was better as it extended comboBox..

    http://www.sencha.com/forum/showthread.php?102903-How-to-create-Combox-with-CheckBox-items&p=482721#post482721

    ReplyDelete
  2. Its not working. It Just shows a TextField !!.

    Can you please help. Why it is happening ?

    ReplyDelete
  3. Can you please tell me which version of GXT you are using?. At least this code should show a "Trigger field", which means a textfield with down-arrow attached to it.

    ReplyDelete
  4. Hi Santosh,
    How can i achieve the same in GXT 3. It does not have CheckBoxListView

    ReplyDelete
  5. Hi Santosh,

    I have used GXT - ComoboBox with Multi select feature in current project it is working fine our end, But i face following events issues:
    - backspace bug,(if i press backspace it going to previous page)
    - numpad bug.(if i press '1,2,3...' from numpad it giving 'a,b,c....' values).
    - comma,dot bug.
    - special characters bug.
    - selection area.

    Please let me know in case any idea.

    Thanks

    ReplyDelete