Table item drag & drop creating null values

Under Vaadin 6, my editable Table allows the rows to be dragged and dropped without errors. Under Vaadin 7, it seems that whenI drag the row, the dragged item has the values I expect, the new item I create seems to have the values set back, but when the Table is re-rendered, the item’s two String properties are set to null (but the Integer property shows the original value from before the drag).

I don’t seem to have this issue with my tables that are backed by bean items. Any ideas what might be wrong with this code?

Here’s where I create the Table:


optionList = new Table();
optionList.setWidth(100, Unit.PERCENTAGE);
optionList.addContainerProperty("order", Integer.class, null);
optionList.addContainerProperty("label", String.class, null);
optionList.addContainerProperty("value", String.class, null);
optionList.setColumnHeaders( vaadinUi.getStringArray("LibDropDownVersionForm.optionList.columnHeaders") );
optionList.setColumnAlignment("order", Align.CENTER);
optionList.setColumnExpandRatio("order", 0f);
optionList.setColumnExpandRatio("label", 0.25f);
optionList.setColumnExpandRatio("value", 0.75f);
optionList.setSortEnabled(false);
optionList.setNullSelectionAllowed(true);
optionList.setSelectable(true);
optionList.setImmediate(true);

Here’s my table’s field factory (note that the order field is just an Integer and has no actual field, and I’m currently tracking the fields created in my table so I can commit them individually since I guess that’s how I did it back in Vaadin 6):


		optionList.setTableFieldFactory(new DefaultFieldFactory() {
			private static final long serialVersionUID = 2978798748115448927L;

			public Field<?> createField(Container container, Object itemId, Object propertyId, Component uiContext) {
				
		        // If I already have the specified itemId in my map and this is the first property in our table, 
		        // let's clear it and assume we're building it again
		        if ( propertyId.equals("order") ) {
					if ( optionListFields.containsKey(itemId) ) { 
						optionListFields.clear(); 
					}
					return null;
		        }

		        List<Field<?>> fieldList = optionListFields.get(itemId);
		        if ( fieldList == null ) fieldList = new LinkedList<Field<?>>();

		        Field<?> f = super.createField(container, itemId, propertyId, uiContext);
		        f.setBuffered(true);
		        f.setWidth(100,Unit.PERCENTAGE);
		        fieldList.add(f);
		        optionListFields.put(itemId,fieldList);
		        return f;
		    }
		});

Here’s my drop handler:


		optionList.setDropHandler(new DropHandler() {
			private static final long serialVersionUID = 9044808753146836665L;

			@SuppressWarnings("unchecked")
			public void drop(DragAndDropEvent dropEvent) {
                DataBoundTransferable t = (DataBoundTransferable)dropEvent.getTransferable();
                Object sourceItemId = t.getItemId();

                AbstractSelectTargetDetails dropData = ((AbstractSelectTargetDetails)dropEvent.getTargetDetails());
                Object targetItemId = dropData.getItemIdOver();
                
                // No move if source and target are the same
                if ( sourceItemId == targetItemId || targetItemId == null )
                	return;
                
            	commitFieldsToTableOnly();

            	// Let's remove the source of the drag so we can add it back where requested...
                Item sourceItem = optionList.getItem(sourceItemId);
                Integer sourceOrder = (Integer)sourceItem.getItemProperty("order").getValue();
                String sourceOption = (String)sourceItem.getItemProperty("label").getValue();
                String sourceValue = (String)sourceItem.getItemProperty("value").getValue();
                Item newItem;
                optionList.removeItem(sourceItemId);
                if ( dropData.getDropLocation() == VerticalDropLocation.BOTTOM ) {
            		newItem = optionList.addItemAfter(targetItemId,sourceItemId);
            	} else {
                    Object prevItemId = optionList.prevItemId(targetItemId);
                    newItem = optionList.addItemAfter(prevItemId, sourceItemId);
            	}
            	newItem.getItemProperty("order").setValue(sourceOrder);
            	newItem.getItemProperty("label").setValue(sourceOption);
            	newItem.getItemProperty("value").setValue(sourceValue);
// AT THIS POINT, newItem definitely shows all of the correct values -- that is, they are the same as values are retrieved from the sourceItem
            	optionListChanged = true;
            }

            public AcceptCriterion getAcceptCriterion() {
                return new And(SourceIsTarget.get(), new Not(AbstractSelect.VerticalLocationIs.MIDDLE));
            }
        });

But after the drag, the source row has moved to the right location, and it shows the correct order value (the order value from the row that was dragged), but the two String fields are null. When I actually save my data by looping, they loops through optionList.getItemIds() and when I retrieve the 3 properties, they also show null values for the two String properties. I am confused because the item is created by the optionList (Table) via addItem() and I use that returned Item to set the values, so you’d think the Table’s container would have my values and not null. Clearly, I’m missing something that worked in Vaadin 6, but not in Vaadin 7. Thanks!

EDIT: I am using Vaadin 7.0.6

After some debugging, I have no fix, but I can see what is happening in the createField() of the DefaultFieldFactory created for the table.

When I drag the row, it seems that createField() is called multiple times for the same itemId and propertyId. WhenI have just two rows in my table to simplify drag & drop tracking, I note that it will rebuild a couple of times with two item ids (and each of those times it’s actually called 3 times to build the three properties I have in each item), but on the last round, it only calls it for my first item id, and the other item id is not called.

I don’t know what this means since the Table shows both rows, all with TextField for input as expected. But I think this is where my nulls are coming from – even though my container still has two items, the field factory doesn’t seem to build them all.

Because Fields in Tables are not tracked so I can commit/validate them, I have my own tracking of each field as it’s created for the table so that I can commit/validate them. But when I put in TRACE debugs where I seem to get my first property (“order”) and I already have it in my tracking, I clear my tracking and then as each field is created, I add to the tracking. But here’s what it shows just by doing the drag and drop:

TRACE: clearing my option list fields because seem to be starting fresh
TRACE: create field for itemId: 1; propertyId: label
TRACE: create field for itemId: 1; propertyId: value
TRACE: create field for itemId: 2; propertyId: label
TRACE: create field for itemId: 2; propertyId: value
TRACE: clearing my option list fields because seem to be starting fresh
TRACE: create field for itemId: 1; propertyId: label
TRACE: create field for itemId: 1; propertyId: value
TRACE: create field for itemId: 2; propertyId: label
TRACE: create field for itemId: 2; propertyId: value
TRACE: clearing my option list fields because seem to be starting fresh
TRACE: create field for itemId: 1; propertyId: label
TRACE: create field for itemId: 1; propertyId: value

As you can see, the drag and drop seems to cause all of the fields in my Table to be called twice, but on the third round it only creates fields for my first item. This seems like a bug I’ve uncovered as this code worked under Vaadin 6.

Thoughts? Thanks!

Well, that’s not entirely it. That above happens if drag row 2 above row 1.

If I drag row 1 below row 2, the trace shows the fields being created, but the value of my two properties remains set at null:

TRACE: clearing my option list fields because seem to be starting fresh
TRACE: create field for itemId: 2; propertyId: label
TRACE: create field for itemId: 2; propertyId: value
TRACE: clearing my option list fields because seem to be starting fresh
TRACE: create field for itemId: 2; propertyId: label
TRACE: create field for itemId: 2; propertyId: value
TRACE: create field for itemId: 3; propertyId: label
TRACE: create field for itemId: 3; propertyId: value
TRACE: clearing my option list fields because seem to be starting fresh
TRACE: create field for itemId: 2; propertyId: label
TRACE: create field for itemId: 2; propertyId: value
TRACE: create field for itemId: 3; propertyId: label
TRACE: create field for itemId: 3; propertyId: value

Since I remove the item (previously I reused the item id since I was just drag and dropping and there was no item id issue to be concerned about), in this case item 1 since that was dragged, and when I add the new item I give it a new item id (3 in this case), you can see that it seems to create the fields now, but still the item values are not properly tied to the new item row after the drop.

If I drag item 2 above item 1, it again shows the odd field creation, but with the new item numbers since it definitely knows about the items, it’s just not setting it up correctly and in this case fails to create the item id 3 fields:

TRACE: clearing my option list fields because seem to be starting fresh
TRACE: create field for itemId: 1; propertyId: label
TRACE: create field for itemId: 1; propertyId: value
TRACE: create field for itemId: 3; propertyId: label
TRACE: create field for itemId: 3; propertyId: value
TRACE: clearing my option list fields because seem to be starting fresh
TRACE: create field for itemId: 1; propertyId: label
TRACE: create field for itemId: 1; propertyId: value
TRACE: create field for itemId: 3; propertyId: label
TRACE: create field for itemId: 3; propertyId: value
TRACE: clearing my option list fields because seem to be starting fresh
TRACE: create field for itemId: 1; propertyId: label
TRACE: create field for itemId: 1; propertyId: value

What am I doing wrong?

Does anybody have any ideas on this? I am now on Vaadin 7.1.8 and it’s still a problem.

The only solution I currently have is switch to use a bean item container. For whatever reason, when the table is set from values from a bean item container, it seems to work fine.

But when I use Table.addContainerProperty (with all set to use null for the default value for an Integer and two String fields) to define the three properties, along with ‘sourceItem.getItemProperty()’ (sourceItem is the Item retrieved from the Table using the dragged item id). When I get the values this way in the ‘drop’ method, the values are correct. Then I create a new Item and use newItem.getItemProperty(“propname”).setValue() to give those original values to the new Item.

So I’m guessing there’s some subtle error that I’m missing since when I use a bean, the Table has a Container set specifically. When I’m not, it’s as if I’m just referencing the Table directly as if it has its own Container (Table.addContainerProperty()). Is that value for when the Table will contain data that can be updated and drag&dropped?

Just for grins, I changed the item id for the move so it was an entirely new, unique value in the Table, but this had no effect. It still did the drag & drop just fine, but the two String properties in the Item are still set to null even though it all looks fine for the Item in the drop handler (I can see the values for my fields and the item id).

No response from Team Vaadin so I’ll just skip and use a bean item container since there’s clearly a bug in the Table’s built-in indexed container when we drag and drop to reorder the rows.