4

I'm following Goolge's example on how to add ListBoxes/SelectionCells to a CellTable, but I can't figure how to change the behaviour so the matching is done not with the string value displayed.

The items I display @SelectionCell are not unique (i.e there can be 2 elements with the same name), so I need to use other fields associated with the object to know which one was selected

for (IrrigationProgramDTO program: programOptions)    
 categoryNames.add(program.getName());

SelectionCell categoryCell = new SelectionCell(categoryNames);
Column<IrrigationGapDTO, String> categoryColumn = new Column<IrrigationGapDTO, String>    (categoryCell) {
      @Override
      public String getValue(IrrigationGapDTO object) {
          if (object.getProgramSelected()!=null)
              return object.getProgramSelected().getName();
          else
              return "";
      }
    };      
    categoryColumn.setFieldUpdater(new FieldUpdater<IrrigationGapDTO, String>() {
      public void update(int index, IrrigationGapDTO object, String value) {
          for (IrrigationProgramDTO program: programOptions) {
                  //not valid as there could be more than 1 program with the same name
              if (program.getName().equals(value)) { 
                  object.setProgramSelected(program);
                  break;
              }

          }  
      }
jpp1jpp1
  • 181
  • 1
  • 16

3 Answers3

2

Here is my new implementation of solution #3 (note that you must add a FieldUpdater to the column for it to work):

import java.util.Arrays;
import java.util.List;

import com.google.gwt.cell.client.AbstractEditableCell;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.BrowserEvents;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.SelectElement;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.client.SafeHtmlTemplates.Template;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;

/**
 * A {@link Cell} used to render a drop-down list.
 *
 * @author Gaspard van Koningsveld
 */
public class ItemSelectionCell<C> extends AbstractEditableCell<C, C> {

    interface Template extends SafeHtmlTemplates {
        @Template("<select tabindex=\"-1\" style=\"width:100%\">")
        SafeHtml beginSelect();

        @Template("<option value=\"{0}\">{1}</option>")
        SafeHtml deselected(int hash, String option);

        @Template("<option value=\"{0}\" selected=\"selected\">{1}</option>")
        SafeHtml selected(int hash, String option);

        @Template("</select>")
        SafeHtml endSelect();
    }

    private static Template template;

    private List<C> items;

    public ItemSelectionCell(C itemsArray[]) {
        this(Arrays.asList(itemsArray));
    }

    public ItemSelectionCell(List<C> items) {
        super(BrowserEvents.CHANGE);
        if (template == null) {
            template = GWT.create(Template.class);
        }
        this.items = items;
    }

    @Override
    public void onBrowserEvent(Context context, Element parent, C value, NativeEvent event, ValueUpdater<C> valueUpdater) {
        super.onBrowserEvent(context, parent, value, event, valueUpdater);
        if (BrowserEvents.CHANGE.equals(event.getType())) {
            SelectElement select = parent.getFirstChild().cast();
            int newIndex = select.getSelectedIndex();
            valueUpdater.update(items.get(newIndex));
        }
    }

    @Override
    public void render(Context context, C value, SafeHtmlBuilder sb) {
        sb.append(template.beginSelect());
        for (int i = 0; i < items.size(); i++) {
            C item = items.get(i);
            if (item.equals(value)) {
                sb.append(template.selected(i, getItemDisplayString(item)));
            } else {
                sb.append(template.deselected(i, getItemDisplayString(item)));
            }
        }
        sb.append(template.endSelect());
    }

    public String getItemDisplayString(C item) {
        return item.toString();
    }

    public List<C> getItems() {
        return items;
    }

    public void setItems(List<C> items) {
        this.items = items;
    }

    @Override
    public boolean isEditing(Context context, Element parent, C value) {
        return false;
    }
}
qwertzguy
  • 15,699
  • 9
  • 63
  • 66
  • Came back here thanks to google, I had completely forgotten I posted this! Seems to work with a little modification to allow null/empty values – jpp1jpp1 Apr 30 '13 at 16:01
1

3 possible solutions:

1. Dirty workaround:

Instead of getName() return getName() + some unique identifier:

public String getValue(IrrigationGapDTO object) {
    if (object.getProgramSelected()!=null)
        return object.getProgramSelected().getName()+"_"+object.getUniqueIdentiufier();
    else
        return "";
}

then in the FieldUpdater you can split on the "_" character and deal with duplicates

2. Use a unique id instead of getName(): Just generate/assign a unique id to your programms and use it instead of name.

3. Use IrrigationProgramDTO type instead of String:

Instead of String you can use IrrigationProgramDTO class in the Column definition. However you probably have to use a user-defined SelectionCell which takes IrrigationProgramDTO type instead of String as Data-type.

Column<IrrigationGapDTO, IrrigationProgramDTO> categoryColumn = new Column<IrrigationGapDTO, IrrigationProgramDTO>    (categoryCell) {
  @Override
  public IrrigationProgramDTO (IrrigationGapDTO object) {
      if (object.getProgramSelected()!=null)
          return object.getProgramSelected();
      else
          return null;
  }

};      
categoryColumn.setFieldUpdater(new FieldUpdater<IrrigationGapDTO, IrrigationProgramDTO>() {
  public void update(int index, IrrigationGapDTO object, IrrigationProgramDTO value) {
       object.setProgramSelected(program);
      }  
  }
Ümit
  • 17,379
  • 7
  • 55
  • 74
  • Solution #1 is the one I've temporarily implemented, but as you say it's dirty, I don't like the user seeing some strange numbers appended to the program name. #2 cannot be used as the id comes from a DB. I'll check #3. – jpp1jpp1 Mar 13 '12 at 16:22
  • #3 is the better option in my opinion. I would customize using an interface inestead of an object (if possible), to reuse code with future DTO... – gavioto Apr 30 '13 at 16:19
0

Here is my implementation of Solution #3 of @Ümit:

public static abstract class EditSelectColumn<E, S> {

    Map<String, S> selectionMap = new HashMap<String, S>();

    EditColumn<E> column;

    protected final String relationshipFieldName;

    public EditSelectColumn(String relationshipFieldName) {
        this.relationshipFieldName = relationshipFieldName;

        for (S option : getOptions()) {
            assert getOptionString(option) != null : "Option string cannot be null, please check your database";
            selectionMap.put(getOptionString(option), option);
        }
        SelectionCell cell = new SelectionCell(new ArrayList<String>(selectionMap.keySet()));
        column = new EditColumn<E>(cell) {
            @Override
            public String getValue(E object) {
                if (getOption(object) == null)
                    return "";
                return getOptionString(getOption(object));
            }

            @Override
            public void setValue(E object, String value) {
                setOption(object, selectionMap.get(value));
            }
        };
    }

    public EditColumn<E> getColumn() {
        return column;
    }

    public abstract List<S> getOptions();

    public abstract String getOptionString(S option);

    public abstract S getOption(E object);

    public abstract void setOption(E object, S value);
}
qwertzguy
  • 15,699
  • 9
  • 63
  • 66
  • Well actually I read too fast. It is not solution #3 of @Ümit because I don't make my own SelectionCell. Plus it doesn't work if you have multiple times the same string. – qwertzguy Jan 30 '13 at 15:40