Select dependent on another select, possible?

I’m evaluating vaadin for use in my project. One of the requirements I have is for two selects in a table. When the user chooses the value for one, it determines the values for the second select. The requirement is very similar to the example shown here
http://www.smartclient.com/smartgwt/showcase/#grid_editing_dependent_selects

Is this possible in a straightforward manner?
This is all that I’ve come up with and it does not seem to be encouraging.
http://vaadin.com/forum/-/message_boards/view_message/285275

Thanks in advance,
Arvind

Here is an example:



package com.example.seltest;

import java.util.HashMap;

import com.vaadin.Application;
import com.vaadin.data.Container;
import com.vaadin.data.Property;
import com.vaadin.data.Property.*;
import com.vaadin.data.util.IndexedContainer;
import com.vaadin.data.util.MethodProperty;
import com.vaadin.ui.*;

@SuppressWarnings("serial")
public class SeltestApplication extends Application {

	Table t = new Table("Local Data");
	static IndexedContainer divisions;
	static HashMap<String, IndexedContainer> departments = new HashMap<String, IndexedContainer>();
	static {
		divisions = new IndexedContainer();
		for (String d : new String[] { "Marketing", "Services", "Sales" }) {
			divisions.addItem(d);
			IndexedContainer deps = new IndexedContainer();
			departments.put(d, deps);
			for (int i = 0; i < 8; i++) {
				deps.addItem(d + " dept " + i);
			}
		}
	}

	@SuppressWarnings("rawtypes")
	@Override
	public void init() {
		Window mainWindow = new Window("Seltest Application");
		mainWindow.addComponent(t);
		setMainWindow(mainWindow);

		t.addContainerProperty("Name", String.class, "");
		t.addContainerProperty("Division", String.class, "");
		t.addContainerProperty("Department", String.class, "");

		t.addItem(new Object[] { "Richard Smith", "Marketing", "Department" },
				new Integer(1));
		t.addItem(new Object[] { "Sam Edvards", "Marketing", "Support" },
				new Integer(2));

		t.setSelectable(true);

		mainWindow.addComponent(t);
		t.setSizeFull();
		t.setImmediate(true);

		CheckBox editable = new CheckBox("Edit", new MethodProperty(t,
				"editable"));
		editable.setImmediate(true);
		mainWindow.addComponent(editable);

		t.setTableFieldFactory(new TableFieldFactory() {

			public Field createField(Container container, Object itemId,
					Object propertyId, Component uiContext) {
				if ("Name".equals(propertyId))
					return new TextField(null, container.getContainerProperty(
							itemId, propertyId));
				final NativeSelect dropdown = new NativeSelect();
				if ("Division".equals(propertyId))
					dropdown.setContainerDataSource(divisions);
				else {
					final Property divisionProperty = container
							.getContainerProperty(itemId, "Division");
					((Property.ValueChangeNotifier) divisionProperty)
							.addListener(new ValueChangeListener() {
								public void valueChange(ValueChangeEvent event) {
									dropdown.setContainerDataSource(divisionProperty
											.getValue() == null ? null
											: departments.get(divisionProperty
													.toString()));
								}
							});
					dropdown.setContainerDataSource(divisionProperty
							.getValue() == null ? null
							: departments.get(divisionProperty
									.toString()));
				}
				dropdown.setPropertyDataSource(container.getContainerProperty(
						itemId, propertyId));
				dropdown.setImmediate(true);
				return dropdown;
			}
		});

	}

}

Sorry for quick “hack-n-slash” type of coding ;)

Thanks very much for the quick response, Joonas. That doesn’t look too bad at all.

Hi

The Code you posted will mark all the rows as editable. What if I wanted to edit a pirticular row if user selects a roww in the table.
I am quite new to vaadin and we are evaluating this for our project

Regards
Ajay

You can set the whole table to editable, but in the fieldfactory affect how the rows will be rendered. if that method returns null, it will fallback to the property formatter, ie. not editable. You can check if itemId == table.getVaule() then return a field, otherwise return null. You still have to call something that retriggers the field generation in the factory, because that is not automatically done on valuechange.

Hi jens

Can you please elaborate with some code samples. I am giving below the valueChange method which will be called on selection of a table row.
How can I make the this editable with new fields.

[code]

public void valueChange(ValueChangeEvent event) {
if(event.getProperty() instanceof PatientScheduleList){
final PatientSchedule selectedRow = (PatientSchedule) patientScheduleList.getValue(); //patientScheduleList is the Table
patientScheduleList.setImmediate(true);
System.out.println(“creating new field”);
// Here i need to create new Fields for the selected table row
}
}
[/code]
Regards
Ajay bathula

Here is an example of it. Notice that I had to extend Table to have access to the protected methods. The selected row is always editable.

package com.example.playground;

import java.util.Date;

import com.vaadin.Application;
import com.vaadin.data.Container;
import com.vaadin.data.Item;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.util.IndexedContainer;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Component;
import com.vaadin.ui.DefaultFieldFactory;
import com.vaadin.ui.Field;
import com.vaadin.ui.Table;
import com.vaadin.ui.TableFieldFactory;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;

public class PlaygroundApplication extends Application {

    private static final long serialVersionUID = 8672592529682639044L;

    @Override
    public void init() {
        final VerticalLayout mainLayout = new VerticalLayout();
        Window mainWindow = new Window("Example", mainLayout);
        VerticalLayout tableLayout = new VerticalLayout();
        setMainWindow(mainWindow);
        tableLayout.setSizeUndefined();
        final MyTable table = new MyTable();
		table.setContainerDataSource(createContainer());
		table.setSelectable(true);
		table.setEditable(true);
		table.setTableFieldFactory(new TableFieldFactory() {
			private static final long serialVersionUID = 2556203202516702627L;
			DefaultFieldFactory defaultFieldFactory = DefaultFieldFactory.get();
			
			public Field createField(Container container, Object itemId,
					Object propertyId, Component uiContext) {
				if(itemId != null && itemId.equals(table.getValue())){
					return defaultFieldFactory.createField(container, itemId, propertyId, uiContext);
				}
				return null;
			}
		});
		table.addListener(new ValueChangeListener() {
			private static final long serialVersionUID = -8755018821356306602L;

			public void valueChange(ValueChangeEvent event) {
				table.resetPageBuffer();
				table.enableContentRefreshing(true);
			}
		});
		table.setImmediate(true);
        tableLayout.addComponent(table);
        mainLayout.addComponent(tableLayout);
        mainLayout.setComponentAlignment(tableLayout, Alignment.MIDDLE_CENTER);
        mainLayout.setMargin(true);
    }

    public static IndexedContainer createContainer() {
        IndexedContainer container = new IndexedContainer();
        container.addContainerProperty("foo", String.class, null);
        container.addContainerProperty("bar", Date.class, null);
        for (int i = 0; i < 10; i++) {
            Item item = container.addItem(i);
            item.getItemProperty("foo").setValue(i + "");
            item.getItemProperty("bar").setValue(new Date());
        }
        return container;
    }

}

class MyTable extends Table{
	private static final long serialVersionUID = -6704180910830007338L;
	
	@Override
	public void resetPageBuffer() {
		super.resetPageBuffer();
	}
	
	@Override
	protected void refreshRenderedCells() {
		super.refreshRenderedCells();
	}

	@Override
	public void enableContentRefreshing(boolean refreshContent) {
		super.enableContentRefreshing(refreshContent);
	}
}

Hi Jens

The code you posted helped me a lot in understanding the workings of table cell generation. Unfortunately i see that the code will not work if i have generated column in the table. How to approch this problem.

Regards
Ajay Bathula

Okay, so the column generator takes care of generating the components instead of the standard field factory / value formatter. That generateCell -method has also the itemId and columnId properties that I used in the field factory to toggle between editable and read-only. You could probably use the same technique inside generateCell and the fields will most likely be re-generated on valuechange

Hi Jens

Thanks a lot that did worked. Now I can have as many different layouts in table row when a user selects a table row.

Regards
Ajay Bathula:grin:

Good news: Since vaadin 6.7.2,
Table.refreshRowCache()
can be used to refresh the table without extending it.

Bad news (vaadin 7.1.0): If this method is called from the value change listener, the fields for the editable cells are empty. The initially generated fields contain the property values as expected, but after the first value change is issued (row is clicked), the editor fields are empty. As far as I was able to track this down, the value is still there after the field is generated and attached to the property in
Table.bindPropertyToField(…)
. Any ideas where the field values may have gone? Looking at the UIDL response, there are lot’s of
cached: true

Interestingly it works as expected if I replace the call to
Table.refreshRowCache()
with
table.setContainerDataSource(table.getContainerDataSource())
. However, initializing the table completely new on every row click is quite heavy.