Legacy Screen - addItemPropertyChangeListener with collectiondatasource

Hi

I’m not sure if I am misunderstanding something with the addItemPropertyChangeListeners but I can’t seem to get the addItemPropertyChangeListener to fire on a standalone collectiondatasource added to a legacy screen. I have the same listeners on nested collectiondatasources elsewhere in the project and these work fine but a standalone collectiondatasource doesn’t seem to fire. I’ve attached an example project with a listener that doesn’t fire within the customer screen.

listenertest2.zip (859.3 KB)

package com.company.listenertest2.web.screens.customer;

import com.company.listenertest2.entity.CustomerNotes;
import com.haulmont.cuba.gui.components.EntityCombinedScreen;
import com.haulmont.cuba.gui.data.CollectionDatasource;

import javax.inject.Inject;
import java.util.Map;
import java.util.UUID;

public class CustomerBrowse extends EntityCombinedScreen {
@Inject
private CollectionDatasource<CustomerNotes, UUID> customerNotesesDs;

@Override
public void init(Map<String, Object> params) {
    super.init(params);
    customerNotesesDs.addItemPropertyChangeListener(e -> {
        showNotification("Hello");
    });
}

@Override
public void ready() {
    super.ready();
}

}

Thanks in advance
David

Hi,
Why do you expect this listener to fire?
customerNotesesDs is bound only to a non-editable table.

You can expect “item changed” and “state changed” event to be fired, but not “item property changed”.
“Item property changed” is fired when single property of an entity is changed, e.g. by an editable table or by a text field, or by application code.

Hi Alex

I’ve changed the table to also be editable but it has not made any difference. I would have expected the property change listener to fire as the table has 2 actions ‘create’ and ‘edit’ which will be against a single entity as described in your response. I’ve done a little further investigation and can see that the datasource itemchangepropertyevent is being called (which is what I would expect):
image

However it does not to get to the code within itempropertychange block within the screen controller which is what I am really confused with as I can’t see any errors that would prevent it from getting there.

I thought that a nested collectiondatasource and and standalone collectiondatasource should work in exactly the same way for an itempropertychangelistener event. However, if my understanding of this is completely wrong then how do you get a change in the collectiondatasource within the screen controller to get access to the previous value and subsequently run a routine of the back of it.

I’ve attached a new project to show the differences with a nested collectiondatasource and standalone collectiondatasource. Both are to the same entity and both have the same actions against the tables.

listenertest2.zip (885.4 KB)
Actions to reproduce:

  • Add new customer
  • Add new nested customer note
  • Add new standalone customer note
  • Edit nested customer note (on save message is displayed from screen controller change event)
  • Edit standalone customer note (on save nothing is displayed and code is not run in screen controller)

Thanks
David

Hi,

The difference you observe is because of the composition edit mode.
Your com.company.listenertest2.entity.Customer#customerNotes field is marked as @Composition.

When you invoke “Edit” action with nested collection datasource (customerNoteNestedDs) - editor is opened in the composition mode. The entity which is edited in the CustomerNotesEdit - is exactly the same Java object, as stored in the customerNoteNestedDs datasource.

Then, all changes to the item properties are done in the CustomerNotesEdit screen. And this is the screen - CustomerNotesEdit - where all ItemPropertyChanged events should be listened.
But because of the how composition edit mode is implemented - these events are also fired in the CustomerBrowse.

So ItemPropertyChangeEvent fired for the customerNoteNestedDs is the side effect, it’s not how it’s supposed to work. In CustomerBrowse item properties are not changed, so event is not fired.

Non-composition editor works this way:

  • open editor screen, pass current item from the customerNotesesDs
  • reload CustomerNotes entity from the database
  • edit reloaded entity. PropertyChanged events are also fired, but they are fired in the CustomerNotesEdit screen, for the reloaded instance.
  • when the editor is closed, save changes to the database and refresh customerNotesesDs to see the changes.

See more here: https://www.cuba-platform.com/guides/data-modelling-composition

Thanks for the explanation Alex. Unfortunately a core/main entities linked entities must remain non-committed until the main entity is saved so I can’t commit just the note entity to the database and reload. However, your advice has helped as now understand the missing link. I have now managed to get it working by sending the note entity as a parameter through to the note editor screen:

noteTableEdit.setWindowParams(ParamsMap.of(“customerNotes”, customerNotesesDs.getItem()));

and then setting the customerNote entity in the note editor screen:

public class CustomerNotesEdit extends AbstractEditor {
@WindowParam
private CustomerNotes customerNotes;
@Inject
private Datasource customerNotesDs;

@Override
public void ready() {
    super.ready();
    if (customerNotes!=null)
    {
        customerNotesDs.setItem(customerNotes);
    }
}

}

Thanks
David

FYI
This is the logic from EditAction, from legacy GUI:
com.haulmont.cuba.gui.components.actions.EditAction#actionPerform

It opens the screen this way, passing parentDs as one of parameters:

        AbstractEditor window = frameOwner.openEditor(getWindowId(), existingItem, getOpenType(), params, parentDs);

And parentDs is determined this way (“is datasource a nested datasource with composition?”)

            Datasource parentDs = null;
            final CollectionDatasource datasource = target.getDatasource();
            if (datasource instanceof PropertyDatasource) {
                MetaProperty metaProperty = ((PropertyDatasource) datasource).getProperty();
                if (metaProperty.getType().equals(MetaProperty.Type.COMPOSITION)) {
                    parentDs = datasource;
                }
            }