How to set PopupView on a table column?

Every row of that column, has to be a PopupView that dynamically loading nested datasource in a Table.

I only find a manual way to get it, but few problems left:

  1. The dynamic loading of content in
PopupView

is not success. Seems it using lazying fetch. The sampler one has such loading effect, just like an active dynamic fetch.
2. For the lambda expression of

addGeneratedColumn

in sampler, don’t know how to reference the entity of that row? I mean how to rewrite the generator below to a lambda function?
3. Seems the popupView is bound with selected row(entity), it means, if you just opened the table, nothing has been selected, the popup could not fetch related data, if you select row 2, then every popup of other row will show exactly the same content from row2, except you chose specific row. One word, you have to chose specific row, then click popup on that row, then you got the expected behaviour.
4. The original idea is just to hover a hint text like

5 phones

then popup a table shows that five phones, even you don’t have to click.

I pointed this function as a column generator in the column properties editor of table in CUBA’s screen editor.


public Component generatePhonesCell(Contact entity) {
    Set<Phone> phones = entity.getPhones();
    int phonesCount = phones.size();
    if (phonesCount <= 1) {
        Label label = componentsFactory.createComponent(Label.class);
        if (phonesCount == 1) {
            Phone phone = phones.iterator().next();
            String phoneNumber = phone.getCountry().getCountryCallingCode()
                    + " " + phone.getNumber();
            label.setCaption(phoneNumber);
        } else {
            label.setCaption(getMessage("noPhoneMessage"));
        }
        return label;
    } else {
        Table table = componentsFactory.createComponent(Table.class);
        table.setDatasource(phonesDs);
        table.addGeneratedColumn("country", new Table.ColumnGenerator() {
            @Override
            public Component generateCell(Entity entity) {
                Label label = componentsFactory.createComponent(Label.class);
                label.setValue(((Phone)entity).getCountry().getCountryCallingCode1());
                return label;
            }
        });

        VBoxLayout vBox = componentsFactory.createComponent(VBoxLayout.class);
        vBox.add(table);

        PopupView popupView = componentsFactory.createComponent(PopupView.class);
        popupView.setMinimizedValue(String.format(getMessage("phonesCountMessage"), phonesCount));
        popupView.setPopupContent(vBox);
        return popupView;
    }
}

Hello, Kai.

  1. Could you please explain in more details what you meant. What you want to be loaded dynamically?

  2. You can use lambda function for addGeneratedColumn method and reference an entity. For example:


contactsTable.addGeneratedColumn("phones", entity -> {
    Set<Phone> phones = entity.getPhones();
    int phonesCount = phones.size();

    if (phonesCount <= 1) {
        ...
    } else {
        ...
    }
});
  1. The reason for such behavior is a nested datasource, which you use to populate a table in PopupView. Nested datasource contains items associated with a currently selected item in table connected with master datasource. To solve this problem you can build custom datasource in addGeneratedColumn and filter Phone entities with currently passed Contact entity. For example:

CollectionDatasource phonesDs = new DsBuilder(getDsContext())
        .setJavaClass(Phone.class)
        .setViewName(View.LOCAL)
        .setId("phonesDs" + entity.getId())
        .buildCollectionDatasource();
phonesDs.setQuery("select p from demo$Phone p where p.contact.id = '" + entity.getId() + "'");
phonesTable.setDatasource(phonesDs);
  1. To show information by mouse hover you can use a label with html content for its description. For example:

Label label = componentsFactory.createComponent(Label.class);
label.setStyleName(ValoTheme.LABEL_COLORED);
label.setValue(String.format(getMessage("phonesCountMessage"), phonesCount));
StringBuilder sb = new StringBuilder();
for (Phone phone : entity.getPhones()) {
  sb.append("<p>").append(phone.getInstanceName()).append("</p>");
}
label.setDescription(sb.toString());

Please note, that Label#setCaption does nothing. To set a value to a label you have to use Label#setValue method instead.

Let me ask you a question. Do you really need a table inside a PopupView? I would recommend using HTML content inside a PopupView as its more lightweight than a table. Below my version of your screen controller:


public class ContactBrowse extends AbstractLookup {
    @Inject
    private Table<Contact> contactsTable;
    @Inject
    private ComponentsFactory componentsFactory;

    @Override
    public void init(Map<String, Object> params) {
        contactsTable.addGeneratedColumn("phones", entity -> {
            Set<Phone> phones = entity.getPhones();
            int phonesCount = phones.size();

            if (phonesCount <= 1) {
                Label label = componentsFactory.createComponent(Label.class);
                if (phonesCount == 1) {
                    Phone phone = phones.iterator().next();
                    String phoneNumber = phone.getCode() + " " + phone.getNumber();
                    label.setValue(phoneNumber);
                } else {
                    label.setValue(getMessage("noPhoneMessage"));
                }
                return label;
            } else {
                VBoxLayout vBox = componentsFactory.createComponent(VBoxLayout.class);
                vBox.add(createPhonesListLabel(entity));

                PopupView popupView = componentsFactory.createComponent(PopupView.class);
                popupView.setMinimizedValue(String.format(getMessage("phonesCountMessage"), phonesCount));
                popupView.setHideOnMouseOut(false);
                popupView.setPopupContent(vBox);
                return popupView;
            }
        });
    }

    private Component createPhonesListLabel(Contact entity) {
        Label content = componentsFactory.createComponent(Label.class);
        content.setHtmlEnabled(true);
        StringBuilder sb = new StringBuilder();
        sb.append("<ul style=\"margin-right: 1em;\">");
        for (Phone phone : entity.getPhones()) {
            sb.append("<li>").append(phone.getInstanceName()).append("</li>");
        }
        sb.append("</ul>");
        content.setValue(sb.toString());
        return content;
    }
}

You can see the full demo at github.

If you will have any question, feel free to ask. We will gladly provide any help.

Regards,

Gleb

Hi Gleb,

In the above example, how to set image instead of String.format(getMessage(“phonesCountMessage”)

Regards,
Mallik