Master Detail Screen - Programmatic Entity Creation?

Am wondering if it is possible to call the create function of a master detail screen?

I am trying to basically duplicate an entity. Something like follows:

@Named("table.create")
protected CreateAction<Plan> tableCreate;

  @Subscribe("duplicatePlanButton")
    protected void onDuplicatePlanButtonClick(Button.ClickEvent event) {
       tableCreate.setNewEntitySupplier(() -> {
           Plan plan = metadata.create(Plan.class);
           plan.setName("some name");
           return plan;
       });
       tableCreate.execute();
    }

Of course, I am getting the “No such screen error” as there is no edit screen.

Is there any way to do this in a master detail type screen?

Hi,

CreateAction in master-detail screen is assigned a special handler, see MasterDetailScreen#initBrowseCreateAction. That’s why it works but your call to execute() doesn’t. But you can call its actionPerform() method, so the correct modified handler will be invoked.

The following solution works for me:

@UiController("untitled6_Product.browse")
@UiDescriptor("product-browse.xml")
@LookupComponent("table")
@LoadDataBeforeShow
public class ProductBrowse extends MasterDetailScreen<Product> {

    @Named("table.create")
    private CreateAction<Product> tableCreate;
    @Inject
    private GroupTable<Product> table;

    private Product prototype;

    @Subscribe("duplicateBtn")
    public void onDuplicateBtnClick(Button.ClickEvent event) {
        prototype = table.getSingleSelected();
        try {
            tableCreate.actionPerform(event.getButton());
        } finally {
            prototype = null;
        }
    }

    @Subscribe
    public void onInitEntity(InitEntityEvent<Product> event) {
        if (prototype != null) {
            event.getEntity().setName(prototype.getName());
            event.getEntity().setPrice(prototype.getPrice());
        }
    }
}

On “Duplicate” click, I save the currently selected in the table entity as a prototype, and then call action’s actionPerform().
The prototype is used it in the standard InitEntityEvent handler to initialize the new entity instance.

Thanks Konstantin, works great. I was scratching my head on that one!

You wouldn’t happen to know how to also init one-to-many entities through this process? Specifically to avoid the new-entity commit context error?

e.g. say we were trying to duplicate an invoice with all the invoice lines with this type of structure

 <instance id="invoiceDc"
              class="com.company.Invoice" view="plan-view">
        <loader id="invoiceDl"/>
        <collection id="invoiceLinesDc" property="lines">
        </collection>     
    </instance>

Or do I have to create a before commit listener?

In the same InitEntityEvent handler, something like this:

Invoice invoice = event.getEntity();
invoice.setLines(new ArrayList());
invoice.getLines().add(someLine1);
invoice.getLines().add(someLine2);

If you copy lines from another instance, don’t forget to change the reference to invoice in them.

Ah yes - that is what I was doing.

But upon save was getting the “On trying to persist a new entity was found that was not marked cascade persist” error. It also said I could add the new entities to the DataContext which I am not sure how to do - or was wondering if it could be done in the screen class itself…
I was trying to figure out how to get around it without having to create a ‘before insert’ listener. But I have just created one now.

Cheers :smiley:

Yes, try to create your Line entities using DataContext.create() method. See the docs: DataContext.

Awesome, thanks! Have always wondered how to do that…

On another note… I feel like the DataContext doc could do with a bit of an overhaul/update. Doesn’t describe the create method either.