Cuba 7.0 - Studio feature request: Add and create New in Editor screen

I stole the topic from another ticket that has been around since januari 2017. The links below are tickets from 2016 and 2017. Is there an updated cuba 7 example for this add and create new?
Do you think this will become a standard arction?

2 Likes

Hi,
An updated example for CUBA 7 API would look like this:

  1. In screen descriptor - add additional action and button:
    <actions>
        <action id="saveAndNew" caption="Save and New"/>
    </actions>
        <hbox id="editActions" spacing="true">
            <button action="saveAndNew"/>
            ...
        </hbox>
    </layout>
</window>
...
  1. In screen controller - implement saveAndNew action handler. We will copy some code from the com.haulmont.cuba.gui.screen.StandardEditor#commit method, to show generalized notification.
public class DeckEdit extends StandardEditor<Deck> {
    // ...
    @Inject
    private DataContext dataContext;

    @Subscribe("saveAndNew")
    public void onSaveAndNew(Action.ActionPerformedEvent event) {
        // copied from StandardEditor#commit method
        commitChanges()
                .then(() -> {
                    commitActionPerformed = true;
                    if (showSaveNotification) {
                        Entity entity = getEditedEntity();
                        MetadataTools metadataTools = getBeanLocator().get(MetadataTools.NAME);
                        Messages messages = getBeanLocator().get(Messages.NAME);

                        notifications.create(Notifications.NotificationType.TRAY)
                                .withCaption(messages.formatMainMessage("info.EntitySave",
                                        messages.getTools().getEntityCaption(entity.getMetaClass()),
                                        metadataTools.getInstanceName(entity)))
                                .show();
                    }
                    
                    // create new item, set as edited entity
                    Deck newItem = dataContext.create(Deck.class);
                    getEditedEntityContainer().setItem(newItem);
                });
    }
}
1 Like

@AlexBudarov

Finally had time to test it. It works but with the following issues

  1. the inverse relation relation to the parent entity is not set automatically. I can easily manage this myself by getting it from the initial child entity at the start of the onSaveAndNew method and setting it on the newly created instance.

  2. the table is not refreshed when clicking ok or cancel at the end. when clicking ok only the last added child is added to the table, when doing cancel none is added to the table.

I have searched the forum and found many similiar topics on how to refresh data, some using cuba 6 and aftercommithandler. some in cuba 7 using datacontext and others using editaction.

There seems to be quite some tickets about this refreshing and it is not clear to me what solution to use in what circumstances.

What is your recommendation for this case?

Hi,

Basically, there are two ways of refreshing a data table in the Generic UI:

  1. Reload data from the database. Inject a data loader that is connected with a data table’s data container and invoke its load() method. We recommend doing it if you perform some additional calculations with the saved object in the code module after submitting it in the UI.

  2. Adding/replacing the object in the data container that is connected to the data table. Inject the data container and invoke its replaceItem() method passing the object that you want to refresh/display. We recommend it in most cases. If this method doesn’t work, use reloading.

For your case, it might be better to use reloading because you don’t have access to all objects that you created and saved in the editor. But you can try to pass the browser screen’s data container to the editor and add items to it if you want to.

1 Like

Edited after trying to reproduce in minimal example.

@AlexBudarov @belyaev Still don’t have it completely working.

I cannot reload data since this is a composition, so the child entities are not in the db yet. I tried reproducing the behaviour below in a minimal setup and can not reproduce it. The only hint I have is that in the case of clicking ok only the last one is added. any hints welcome…

If I create a number of child entities with the saveAndNew button there are 2 possibilities:

  1. click cancel -> the table is updated correctly

2)instead of clicking save and new for the last child, click ok -> only the last child is added to the table.

I debugged into it and I can see that the parent.children collection only added that last one. It seems the default commitAndClose action replaced the collection and only added the last child.
But! If I then click ok in the parent entity window, and reopen the that parent, I see all the children in the table, including the once that were omitted at first.

    @Subscribe("saveAndNew")
    public void onSaveAndNew(Action.ActionPerformedEvent event) {
        if (validateScreen().isEmpty()) {
            Parent parent = getEditedEntity().getParent();
            parent.getChildren().add(getEditedEntity());
            commitChanges()
                    .then(() -> {
                        commitActionPerformed = true;
                        if (showSaveNotification) {
                            Entity entity = getEditedEntity();
                            MetadataTools metadataTools = getBeanLocator().get(MetadataTools.NAME);
                            Messages messages = getBeanLocator().get(Messages.NAME);

                            notifications.create(Notifications.NotificationType.TRAY)
                                    .withCaption(messages.formatMainMessage("info.EntitySave",
                                            messages.getTools().getEntityCaption(entity.getMetaClass()),
                                            metadataTools.getInstanceName(entity)))
                                    .show();
                        }
                    });
            // create new item, set as edited entity
            Child child = dataContext.create(Child.class);
            child.setParent(parent);
            getEditedEntityContainer().setItem(newItem);
        }
    }

after deeper debugging, I can see that the problem of overwriting happens in the updateMasterCollection()

                masterCollection.clear();
                masterCollection.addAll(newCollection);
doOnRemoveItem:83, ObservableList (com.haulmont.cuba.gui.model.impl)
clear:221, ObservableList (com.haulmont.cuba.gui.model.impl)
updateMasterCollection:109, CollectionPropertyContainerImpl (com.haulmont.cuba.gui.model.impl)
updateMaster:87, CollectionPropertyContainerImpl (com.haulmont.cuba.gui.model.impl)
lambda$getMutableItems$0:66, CollectionPropertyContainerImpl (com.haulmont.cuba.gui.model.impl)
accept:-1, 222214788 (com.haulmont.cuba.gui.model.impl.CollectionPropertyContainerImpl$$Lambda$1887)
fireCollectionChanged:69, ObservableList (com.haulmont.cuba.gui.model.impl)
add:105, ObservableList (com.haulmont.cuba.gui.model.impl)
lambda$buildEditor$0:124, EditorBuilderProcessor (com.haulmont.cuba.gui.builders)
accept:-1, 2146683924 (com.haulmont.cuba.gui.builders.EditorBuilderProcessor$$Lambda$1024)
publish:170, EventHub (com.haulmont.bali.events)
fireEvent:128, Screen (com.haulmont.cuba.gui.screen)
close:343, Screen (com.haulmont.cuba.gui.screen)
lambda$closeWithCommit$6:588, StandardEditor (com.haulmont.cuba.gui.screen)
get:-1, 460862587 (com.haulmont.cuba.gui.screen.StandardEditor$$Lambda$1882)
compose:39, SuccessOperationResult (com.haulmont.cuba.gui.util)
closeWithCommit:588, StandardEditor (com.haulmont.cuba.gui.screen)
commitAndClose:553, StandardEditor (com.haulmont.cuba.gui.screen)
accept:-1, 1476096141 (com.haulmont.cuba.gui.screen.StandardEditor$$Lambda$1013)
publish:170, EventHub (com.haulmont.bali.events)
actionPerform:222, BaseAction (com.haulmont.cuba.gui.components.actions)
buttonClicked:67, WebButton (com.haulmont.cuba.web.gui.components)

@AlexBudarov @belyaev
Ok, after even more debugging and comparing I finally can reproduce the issue in a minimal setup.

I created a sample at https://gitlab.com/tom.monnier/cubasaveandnewissue

the key is actually in the entity class, I have a habit of initializing collections. But this actually produces the behavior.

To reproduce create a parent entity. Then create one or more child entities using the save and new button and end with clicking the ok button. If the collection is initialized to the empty list you will see only the last added entity is shown.

If you remove the collection initializer you can add children on an existing parent. But, in that case the collection is not initialized and you get a NullPointerException when adding children on a new Parent. (because I have to add the children to the collection which is null)

    @Composition
    @OnDelete(DeletePolicy.CASCADE)
    @OneToMany(mappedBy = "parent")
    protected List<Child> children = new ArrayList<>();
    //protected List<Child> children;

I will take a look. Can’t offer something quickly.

But I suppose that if you need master/detail screen support, then you need to customize CreateAction / EditAction or use some of their delegates to invoke additional logic (add more items to parent data container).

Hi,
I’ve managed to make your sample work, for Create action.
The result is here:

Key things:

  • remember committed entities in the list
  • use “afterCloseHandler” to add remembered entities to the data container
  • distinguish between OK and Cancel closing action of the child editor. If child editor was closed with OK button, then standard CreateAction logic will automatically append the last created item to the child container, so we need to insert other items before it.