Best way of setting

Hi,

I have an entity Account which has a composition of comments. I have created an edit screen with two tabs: one for the account details and one that shows the comments and you can add a comment as well. To add a comment I have a separate datasource. What is the best way of setting the account for the comment datasource based on the account that is being edited?

Regards, Edwin.

Hi Edwin,

First, decide whether to use nested or non-nested datasources. Please see this topic.

If you go for non-nested datasources, you should link them (i.e. set the edited account as a a parameter of comments query). You can do it declaratively using “ds$” prefix for the parameter:


<datasource id="customerDs"
            class="com.company.sales.entity.Customer"
            view="_local"/>
<collectionDatasource id="ordersDs"
                      class="com.company.sales.entity.Order"
                      view="_local">
    <query>
        <![CDATA[select o from sales$Order o where o.customer.id = :ds$customerDs]]>
    </query>
</collectionDatasource>

In this case, no code is needed.
Alternatively, you can use a “custom$” parameter prefix and pass the parameter value in the Datasource.refresh() method:


<collectionDatasource id="ordersDs"
                      class="com.company.sales.entity.Order"
                      view="_local">
    <query>
        <![CDATA[select o from sales$Order o where o.customer.id = :custom$customer]]>
    </query>
</collectionDatasource>

@Inject
CollectionDatasource<Order, UUID> ordersDs;

@Override
public void init(Map<String, Object> params) {
    ordersDs.refresh(ParamsMap.of("customer", getItem()));
}

The information on datasource query parameters is located here: Query Parameters - CUBA Platform. Developer’s Manual

1 Like

Hi Konstantin,

I understand the concept (or at least I hope) of collection datasources linked to the master entity or using nested datasources as an alternative and this is working for me. What I do not understand is how to link a single datasource (in this case Comment) to add a Comment to the collection. Maybe I can illustrate what I have done so far in the XML below. The intention is when I click the addButton that the comment is added, however the “account” field in the Comment that is associated to the Account is not set. I wonder how I can best set that.


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        caption="msg://editCaption"
        class="com.company.test.web.account.AccountEdit"
        datasource="accountDs"
        focusComponent="fieldGroup"
        messagesPack="com.company.test.web.account">
    <dsContext>
        <datasource id="accountDs"
                    class="com.company.test.entity.Account"
                    view="account-view">
            <collectionDatasource id="commentsDs"
                                  property="comments"/>
        </datasource>
        <datasource id="commentDs"
                    class="com.company.test.entity.Comment"/>
    </dsContext>
    <layout expand="tabSheet"
            spacing="true">
        <tabSheet id="tabSheet">
            <tab id="accountDetails"
                 caption="Account Details"
                 margin="true,false,false,false">
                <fieldGroup id="fieldGroup"
                            datasource="accountDs"
                            height="100%">
                    <column width="200px">
                        <field id="name"/>
                    </column>
                </fieldGroup>
                <table id="commentsTable"
                       height="100%"
                       width="100%">
                    <columns>
                        <column id="note"/>
                    </columns>
                    <rows datasource="commentsDs"/>
                </table>
            </tab>
            <tab id="comments"
                 caption="Comments">
                <vbox>
                    <textField id="noteField"/>
                    <textField id="accountId"
                               datasource="commentDs"
                               property="account"/>
                    <button id="addButton"
                            caption="Add"
                            invoke="onAddButtonClick"/>
                    <table id="notesTable"
                           height="200px"
                           width="100%">
                        <columns>
                            <column id="note"/>
                        </columns>
                        <rows datasource="commentsDs"/>
                    </table>
                </vbox>
            </tab>
        </tabSheet>
        <frame id="windowActions"
               screen="editWindowActions"/>
    </layout>
</window>

I hope that makes my question a little more clear.

Regards, Edwin.

Oh, I see. Datasource.setItem() should be enough.
Let me know if you need a complete code example.

Hi Konstantin,

A complete code example would be appreciated as I am still struggling to grasp the concepts, especially if there are multiple data sources involved that is not the main data source of the AbstractEditor.

Regards, Edwin.

Please provide your onAddButtonClick() method and the fields section of your screen controller.

Hi Konstantin,

I had a stab at it myself but I get a NullPointerException on the item.setAccount. Here’s my code so far.

Screen XML:


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        caption="msg://editCaption"
        class="com.company.test.web.account.AccountEdit"
        datasource="accountDs"
        focusComponent="fieldGroup"
        messagesPack="com.company.test.web.account">
    <dsContext>
        <datasource id="accountDs"
                    class="com.company.test.entity.Account"
                    view="account-view">
            <collectionDatasource id="commentsDs"
                                  property="comments"/>
        </datasource>
        <datasource id="commentDs"
                    class="com.company.test.entity.Comment"/>
    </dsContext>
    <layout expand="tabSheet"
            spacing="true">
        <tabSheet id="tabSheet">
            <tab id="accountDetails"
                 caption="Account Details"
                 margin="true,false,false,false">
                <fieldGroup id="fieldGroup"
                            datasource="accountDs"
                            height="100%">
                    <column width="200px">
                        <field id="name"/>
                    </column>
                </fieldGroup>
                <table id="commentsTable"
                       height="100%"
                       width="100%">
                    <columns>
                        <column id="note"/>
                    </columns>
                    <rows datasource="commentsDs"/>
                </table>
            </tab>
            <tab id="comments"
                 caption="Comments">
                <vbox>
                    <textField id="noteField"/>
                    <button id="addButton"
                            caption="Add"
                            invoke="onAddButtonClick"/>
                    <table id="notesTable"
                           height="200px"
                           width="100%">
                        <columns>
                            <column id="note"/>
                        </columns>
                        <rows datasource="commentsDs"/>
                    </table>
                </vbox>
            </tab>
        </tabSheet>
        <frame id="windowActions"
               screen="editWindowActions"/>
    </layout>
</window>

Screen Controller:


/*
 * Copyright (c) 2016 test
 */
package com.company.test.web.account;

import com.haulmont.cuba.gui.components.AbstractEditor;

import javax.inject.Inject;

import com.company.test.entity.Account;
import com.company.test.entity.Comment;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.data.Datasource;

/**
 * @author Edwin
 */
public class AccountEdit extends AbstractEditor<Account> {

	@Inject
	private Datasource<Comment> commentDs;

    public void onAddButtonClick(Component source) {
    	Comment item = commentDs.getItem();
    	item.setAccount(getItem());
    	commentDs.setItem(item);
    	commentDs.commit();
    	
    	showNotification("Add button clicked!", getItem().getId().toString(), NotificationType.TRAY_HTML);
    }
}

I think you don’t need a separate datasource for creating a Comment at all. You can directly use a value of the TextField, so the method may look as follows:


public void onAddButtonClick(Component source) {
	Comment comment = metadata.create(Comment.class);
	comment.setNote((String) noteField.getValue());
	comment.setAccount(getItem());
	commentsDs.addItem(comment);
}

The created and added to commentsDs entity will be saved to the database on screen commit - all “dirty” datasources will be flushed automatically.
See also this example app: GitHub - cuba-platform/sample-data-manipulation: DEPRECATED. See https://www.cuba-platform.com/guides/intro-working-with-data-in-cuba. It also available via Studio samples tab. The customer-browse.xml screen does similar things.

Hi Konstantin,

That clearly works. Thanks for that. I noticed that if I do a commentsDs.commit() after adding the item the screen still requires a commit which I have added. Is there an alternative to just get the comment that I just added committed without having to do a screen commit?

Regards, Edwin.

Probably this is because you have the composition of datasources. When you add an item to the nested datasource, both of them becomes dirty (because the master owns the collection of nested). But you explicitly commit only the nested one. When you close the screen, it warns you about uncommitted changes in master.
If you really don’t need a composition (in CUBA terms) of entities, use separate datasources linked with a parameter as in my first comment. Then when you add an item to the Comments datasource the Account datasource will remain untouched.