3.5.3.4.3. Dependencies Between Data Components

Sometimes you need to load and display data which depends on other data in the same screen. For example, on the screenshot below the left table displays the list of orders and the right one displays the list of lines of the selected order. The right list is refreshed each time the selected item in the left list changes.

dep data comp
Figure 26. Dependent Tables

In this example, the Order entity contains the orderLines attribute which is a one-to-many collection. So the simplest way to implement the screen is to load the list of orders with a view containing the orderLines attribute and use a property container to hold the list of dependent lines. Then bind the left table to the master container and the right table to the property container.

But this approach has the following performance implication: you will load lines for all orders from the left table, even though you display the order lines only for a single order at a time. And the longer the list of orders is, the more unneeded data is loaded, because there is a little chance that the user will go through all orders to see their lines. This is why we recommend using property containers and wide views only when loading a single master item, for example in an order editor screen.

Also, the master entity may have no direct property pointing to the dependent entity. In this case, the above approach with property container would not work at all.

The common approach to organize relations between data in a screen is to use queries with parameters. The dependent loader contains a query with a parameter which links data to the master, and when the current item in the master container changes, you set the parameter and trigger the dependent loader.

Below is an example of the screen which has two dependent container/loader pairs and the tables bound to them.

<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd">
    <data>
        <collection id="ordersDc" (1)
                    class="com.company.sales.entity.Order" view="order-with-customer">
            <loader id="ordersDl">
                <query>select e from sales_Order e></query>
            </loader>
        </collection>

        <collection id="orderLinesDc" (2)
                    class="com.company.sales.entity.OrderLine" view="_local">
            <loader id="orderLinesDl">
                <query>select e from sales_OrderLine e where e.order = :order</query>
            </loader>
        </collection>
    </data>
    <layout>
        <hbox id="mainBox" width="100%" height="100%" spacing="true">
            <table id="ordersTable" width="100%" height="100%"
                   dataContainer="ordersDc"> (3)
                <columns>
                    <column id="customer"/>
                    <column id="date"/>
                    <column id="amount"/>
                </columns>
                <rows/>
            </table>
            <table id="orderLinesTable" width="100%" height="100%"
                   dataContainer="orderLinesDc"> (4)
                <columns>
                    <column id="product"/>
                    <column id="quantity"/>
                </columns>
                <rows/>
            </table>
        </hbox>
    </layout>
</window>
1 Master container and loader.
2 Dependent container and loader.
3 Master table.
4 Dependent table.
package com.company.sales.web.order;

import com.company.sales.entity.Order;
import com.company.sales.entity.OrderLine;
import com.haulmont.cuba.gui.model.CollectionLoader;
import com.haulmont.cuba.gui.model.InstanceContainer;
import com.haulmont.cuba.gui.screen.*;
import javax.inject.Inject;

@UiController("order-list")
@UiDescriptor("order-list.xml")
@LookupComponent("ordersTable")
public class OrderList extends StandardLookup<Order> { (1)

    @Inject
    private CollectionLoader<Order> ordersDl;
    @Inject
    private CollectionLoader<OrderLine> orderLinesDl;

    @Subscribe
    protected void onBeforeShow(BeforeShowEvent event) {
        ordersDl.load(); (2)
    }

    @Subscribe(id = "ordersDc", target = Target.DATA_CONTAINER)
    protected void onOrdersDcItemChange(InstanceContainer.ItemChangeEvent<Order> event) {
        orderLinesDl.setParameter("order", event.getItem()); (3)
        orderLinesDl.load();
    }
}
1 The screen controller class has no @LoadDataBeforeShow annotation, so the loaders will not be triggered automatically.
2 The master loader is triggered in the BeforeShowEvent handler.
3 In the ItemChangeEvent handler of the master container, a parameter is set to the dependent loader and it is triggered.