How to use a generic data source in a datagrid or table component?

Hi,
I wish to create a screen where the user will be able to use a kind of an SQL Query editor. So, there is a “Source Code Editor” control where the sql statement is defined and an “Execute” button that calls a service responsible to execute the query over a timeseries database. What I need now is to populate a dataGrid with the results of the query - since the columns are not predefined I was wondering what is the most efficient way to create a datagrid with columns that are recreated in runtime? Is there any similar project in CUBA that might help me achieve this functionality?

Regards,
George

Hi George,

You should return a list of KeyValueEntity instances from your data loading service and create KeyValue Container and DataGrid dynamically.

Below is an example of a screen where this is done when a user clicks the button.

<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
        caption="msg://demoScreen.caption"
        messagesPack="com.company.kvtable.web.screens">
    <data>
    </data>
    <layout expand="tableBox" spacing="true">
        <button id="demoBtn" caption="Re-create table"/>
        <vbox id="tableBox" width="100%"/>
    </layout>
</window>
package com.company.kvtable.web.screens;

import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.gui.UiComponents;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.components.data.datagrid.ContainerDataGridItems;
import com.haulmont.cuba.gui.components.data.table.ContainerTableItems;
import com.haulmont.cuba.gui.model.DataComponents;
import com.haulmont.cuba.gui.model.KeyValueCollectionContainer;
import com.haulmont.cuba.gui.screen.*;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;

@UiController("kvtable_DemoScreen")
@UiDescriptor("demo-screen.xml")
public class DemoScreen extends Screen {

    @Inject
    private VBoxLayout tableBox;
    @Inject
    private DataComponents dataComponents;
    @Inject
    private UiComponents uiComponents;

    @Subscribe("demoBtn")
    public void onDemoBtnClick(Button.ClickEvent event) {
        tableBox.removeAll();

        DataGrid<KeyValueEntity> dataGrid = uiComponents.create(DataGrid.class);
        dataGrid.setHeight("100%");
        dataGrid.setWidth("100%");
        tableBox.add(dataGrid);

        List<KeyValueEntity> entities = loadData();

        KeyValueCollectionContainer container = dataComponents.createKeyValueCollectionContainer();
        container.addProperty("name", String.class);
        container.addProperty("number", Integer.class);
        container.setItems(entities);

        dataGrid.addColumn("name", container.getEntityMetaClass().getPropertyPath("name"));
        dataGrid.addColumn("number", container.getEntityMetaClass().getPropertyPath("number"));
        dataGrid.setItems(new ContainerDataGridItems<>(container));
    }

    private List<KeyValueEntity> loadData() {
        ArrayList<KeyValueEntity> entities = new ArrayList<>();

        KeyValueEntity entity = new KeyValueEntity();
        entity.setValue("name", "abc");
        entity.setValue("number", 1);
        entities.add(entity);

        entity = new KeyValueEntity();
        entity.setValue("name", "def");
        entity.setValue("number", 2);
        entities.add(entity);

        return entities;
    }
}

Thank you very much Konstantin!
This is what I was looking for. Just a question regarding the datagrid. Should the result set of returned entities is very large, is there any way for the control to load these entries asynchronously or partially?

Regards,
George

The DataGrid component display all records from the connected data container. So your loadData() method should receive a page number and load entities accordingly.

Thank you very much!