Filterable="true" in composition generates "IllegalArgumentException: Null reference passed as parameter"

A very old error.
If in a composition showed in a detail view the column has filterable=“true”, an IllegalArgumentException is thrown.
Attached you find a project, where you see this error by trying to edit one catalog entry.
If you change in

catalog-detail-view.xml

in the typeDataGrid

<column property="catalog" filterable="true"/>
to
<column property="catalog" filterable="false"/>
there is no error.

Regards
Felix
CompKey.zip (1.0 MB)

Hi!

Unfortunately, I can’t find the test project.

Dmitriy

Hi Dmitry

Sorry, you find it now :wink:

Best regards

Felix

I have looked at your project, this is not a bug.

The reason

The filterable property created a DataGridHeaderFilter component instead of a standard header.

This problem occurs because in this case a CollectionPropertyContainer named as typeDc is used for the child collection (and as dataGrid dataContainer source).
This container is nested in the original catalogDc and doesn’t have its own dataLoader.

The dataLoader is required for DataGridHeaderFilter, because this component works through modifying a JPQL query.
In fact, this problem is related not only with DataGridHeaderFilter, but also with other component that use the dataLoader: pagination, genericFilter, propertyFilter etc.

All of the listed components require dataLoader during initialization, because their operating principle is also based on modifying a JPQL query.

Hi Dmitriy

Thank you for your investigations !

You don’t think, there could be a better error message ?

Would it be possible to have the expected behaviour, and if yes; how ?

Best regards

Felix

The error message is correct. In fact, null is passed as a parameter to the DataGridHeaderFilter constructor. This can be seen by opening the exception details and looking at the stacktrace.

There is one way to solve this problem.

Possible solution:

In general, there is a workaround for this problem: define a separate collection container.
I will show you an example of the OrderOrderItems (1:M) data model

    <data>
        <instance id="orderDc"
                  class="com.company.master.entity.Order">
            <fetchPlan extends="_base">
                <property name="customer" fetchPlan="_instance_name"/>
            </fetchPlan>
             <!-- instead of this collection -->
            <!--<collection id="orderItemsDc" property="orderItems"/>-->
            <loader id="orderDl"/>
        </instance>
        <!-- define this one -->
        <collection id="orderItemsDc" class="com.company.master.entity.OrderItem">
            <loader>
                <query>
                    <![CDATA[select e from OrderItem e where e.order = :container_orderDc]]>
                </query>
            </loader>
        </collection>
    </data>

In this case, a collection of OrderItem entities will be loaded where order attribute is the current Order that opened for editing.

However, this approach has problems.

Problems

This method has two significant problems:

  1. When trying to create a new element of a child entity, the parent entity will not be assigned to this element.
  2. You can still manually assign the parent entity when creating a child element. However, this will create a new problem.
    Let’s imagine that we create a new Order and want to create OrderItems for it right away. In this case, the parent set for new OrderItems will be null. And this is wrong.

Fixing problems

All these problems are solved by passing the parent DataContext to the child entity details views. This can be done as follows:

    @Autowired
    private DialogWindows dialogWindows;
    @Autowired
    private DataManager dataManager;
    @ViewComponent
    private DataGrid<OrderItem> orderItemsDataGrid;


    @Subscribe("orderItemsDataGrid.create")
    public void onOrderItemsDataGridCreate(final ActionPerformedEvent event) {
        OrderItem orderItem = dataManager.create(OrderItem.class);
        orderItem.setOrder(getEditedEntity());

        dialogWindows.detail(orderItemsDataGrid)
                .newEntity(orderItem)
                .withParentDataContext(getViewData().getDataContext())
                .open();
    }

    @Subscribe("orderItemsDataGrid.edit")
    public void onTasksDataGridEdit(final ActionPerformedEvent event) {
        OrderItem selectedItem = orderItemsDataGrid.getSingleSelectedItem();
        if (selectedItem == null) {
            return;
        }

        dialogWindows.detail(orderItemsDataGrid)
                .editEntity(selectedItem)
                .withParentDataContext(getViewData().getDataContext())
                .open();
    }

Here I simply override the default implementation of the actions to pass the parent DataContext.
You can do the sample for your CategoryDetailView view. This way you can take advantage of all the component that use query modification in their implementation.

Regards,
Dmitriy

Hi @d.kremnev

Thank you for your explanations. As JMix is a great tool, I was wondering, that it did generate a not working code out of the box.

After adapting your suggestions, I have now a problem when I want to edit the Type.
grafik

It seems to me, that since 2.5 there is a mandatory field id ( the unique id of the catalog is the catalogName ) ?

How to solve this ( project attached ).

CompKey.zip (1.0 MB)

Thank you