Step 9: Implementing dialogs

This step teaches you how to implement dialogs. You will also learn how to reuse an existing PMO.

Now we will implement the actual functionality of the add button, to add new addresses. When the user clicks on the add button, they will be prompted to enter the address data in a dialog. The entry will be added when the user finishes editing by clicking on the OK button.

dialog initial
Figure 1. Add Address dialog

Reusing a PMO

The content of the dialog should be the same as AddressRowPmo without the delete button. To avoid writing the same PMO twice, reuse it as follows:

  1. Extract a super class from AddressRowPmo such that it contains everything except the delete button and the consumer and call it AddressPmo.

  2. Annotate AddressPmo and make sure the constructor has an Address argument that is assigned to the field address.

The finished implementations of AddressRowPmo and AddressPmo should look like this:

AddressRowPmo.java
public class AddressRowPmo extends AddressPmo {

    private Consumer<Address> deleteConsumer;

    public AddressRowPmo(Address address, Consumer<Address> deleteConsumer) {
        super(address);
        this.deleteConsumer = deleteConsumer;
    }

    @BindReadOnlyBehavior(value = ReadOnlyBehaviorType.WRITABLE)
    @UITableColumn(width = 50)
    @UIButton(position = 60, captionType = CaptionType.NONE, showIcon = true, icon = VaadinIcon.TRASH, variants = {
            ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SMALL })
    public void deleteButton() {
        deleteConsumer.accept(address);
    }
}
AddressPmo.java
@UISection
public class AddressPmo {

    protected Address address;

    public AddressPmo(Address address) {
        this.address = address;
    }

    @ModelObject
    public Address getAddress() {
        return address;
    }

    @UITextField(position = 10, label = "Street", modelAttribute = "street")
    public void street() {
        // model binding
    }

    @UITextField(position = 20, label = "Number", modelAttribute = "streetNumber")
    public void streetNumber() {
        // model binding
    }

    @UITextField(position = 30, label = "Postal Code", modelAttribute = "postalCode")
    public void postalCode() {
        // model binding
    }

    @UITextField(position = 40, label = "City", modelAttribute = "city")
    public void city() {
        // model binding
    }

    @UITextField(position = 50, label = "Country", modelAttribute = "country")
    public void country() {
        // model binding
    }
}

Implementing the dialog

Now switch to the AddressPage to implement the dialog.

First, create a void method called updateUI() and implement it with getBindingContext().uiUpdated().

Then, create a new method called createNewAddress with the signature void createNewAddress() and implement it as follows:

  1. Create a new Address instance and assign it to a variable address.

  2. Create a new AddressPmo instance with address as the argument and store it in a variable dialogPmo.

  3. Assign a lambda expression, where address is added to the partner using the method addAddress, to a variable addHandler of type Handler.

  4. Assign a lambda expression, where partner is saved in the partnerRepository, to a variable saveHandler of type Handler.

  5. Assign addHandler.andThen(saveHandler).andThen(this::updateUI) to a variable okHandler of type Handler.

  6. Assign a new instance of OkCancelDialog to a variable addressDialog by calling the method PmoBasedDialogFactory.newOkCancelDialog, which takes a String, a Handler and a PMO as arguments:

    1. The String is the title of the dialog and should be set to "Add Address".

    2. The Handler should be okHandler and the PMO dialogPmo.

  7. Set the width of addressDialog to "25em" using the method OkCancelDialog.setWidth.

  8. Open addressDialog by calling the method OkCancelDialog.open.

The finished implementation should look like this:

AddressPage.java
public void createNewAddress() {
    Address address = new Address();
    AddressPmo dialogPmo = new AddressPmo(address);

    Handler addHandler = () -> partner.addAddress(address);
    Handler saveHandler = () -> partnerRepository.saveBusinessPartner(partner);
    Handler okHandler = addHandler.andThen(saveHandler).andThen(this::updateUI);

    OkCancelDialog addressDialog =
            new PmoBasedDialogFactory()
                    .newOkCancelDialog("Add Address", okHandler, dialogPmo);
    addressDialog.setWidth("25em");
    addressDialog.open();
}

public void updateUI() {
    getBindingContext().uiUpdated();
}

Updating the functionality of the add button

Now that the dialog is implemented, you need to update the content of the method add() in AddressTablePmo such that it calls createNewAddress. Implement it as follows:

  1. Add a new argument of type Handler to the constructor of AddressTablePmo and assign it to a field createHandler.

  2. Call createHandler.apply in the method add().

  3. Remove the notification.

The finished implementation should look like this:

AddressTablePmo.java
private Handler createHandler;

public AddressTablePmo(Supplier<List<? extends Address>> modelObjectsSupplier, Consumer<Address> deleteAddress,
        Handler createHandler) {
    super(modelObjectsSupplier);
    this.deleteAddress = deleteAddress;
    this.createHandler = createHandler;
}

@SectionHeader
@UIButton(...)
public void add() {
    createHandler.apply();
}

Now, pass createNewAddress to the instance of AddressTablePmo in the method createContent in AddressPage.

The finished implementation should look like this:

AddressPage.java
@Override
public void createContent() {
    addSection(new AddressTablePmo(partner::getAddresses, this::deleteAddress, this::createNewAddress));
}

If you run your application and click on the add button, you should see a dialog with the same fields as the addresses table. If you click on the button "OK", you should now see a new address in the table with the information entered in the dialog.

dialog initial
Figure 2. Add Address dialog
address new line
Figure 3. New Address added

The next step extends the UI such that an address cannot be added if a field is empty.