Cuba Frontend Generator - Entity Management - Editor Component Not Rendering for Entity Relations

Hi,

I have an entity that I have tried generating a React App using the Cuba Frontend CLI.
The following are the steps that I execute:

  1. Generate EntityMangement with the CLI
  2. Run the React App
  3. Go to the entity Browser Page and select a record and Click Edit
  4. Page becomes empty

In the Browser Console I get the following Errors:

index.js:1437 Warning: You cannot set a form field before rendering a field associated with the value.
console. @ index.js:1437
index.js:1437 Warning: You cannot set a form field before rendering a field associated with the value.
console. @ index.js:1437
index.js:1437 Warning: You cannot set a form field before rendering a field associated with the value.
console. @ index.js:1437
index.js:1437 Warning: You cannot set a form field before rendering a field associated with the value.
console. @ index.js:1437
index.js:1437 Warning: You cannot set a form field before rendering a field associated with the value.
console. @ index.js:1437
index.js:1437 Warning: You cannot set a form field before rendering a field associated with the value.
console. @ index.js:1437
index.js:1437 Warning: You cannot set a form field before rendering a field associated with the value.
console. @ index.js:1437
index.js:1437 Warning: You cannot set a form field before rendering a field associated with the value.
console. @ index.js:1437
index.js:1437 Warning: You cannot set a form field before rendering a field associated with the value.
console. @ index.js:1437
index.js:1437 Warning: You cannot set a form field before rendering a field associated with the value.
console. @ index.js:1437
index.js:1437 Warning: Failed prop type: Invalid prop value supplied to Select.
in Select (created by Context.Consumer)
in Select (created by _class)
in _class (created by inject-_class-with-mainStore)
in inject-_class-with-mainStore (created by _class)
in span (created by Context.Consumer)
in div (created by Context.Consumer)
in div (created by Context.Consumer)
in Col (created by Context.Consumer)
in div (created by Context.Consumer)
in Row (created by Context.Consumer)
in FormItem (created by _class)
in _class (created by inject-_class-with-mainStore)
in inject-_class-with-mainStore (created by _class)
in _class (created by inject-_class-with-mainStore)
in inject-_class-with-mainStore (at BeneficiaryBankAccountEditor.tsx:152)
in form (created by Context.Consumer)
in Form (at BeneficiaryBankAccountEditor.tsx:130)
in div (created by Context.Consumer)
in div (created by Context.Consumer)
in Card (at BeneficiaryBankAccountEditor.tsx:129)
in BeneficiaryBankAccountEditorComponent (created by Form(BeneficiaryBankAccountEditorComponent))
in Form(BeneficiaryBankAccountEditorComponent)
in Unknown (created by Context.Consumer)
in injectIntl(Component) (at BeneficiaryBankAccountPage.tsx:19)
in BeneficiaryBankAccountPage (created by Route)
in Route (at App.tsx:78)
in Switch (at App.tsx:75)
in main (created by Basic)
in Basic (created by Context.Consumer)
in Adapter (at App.tsx:74)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Adapter (at App.tsx:73)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Adapter (at App.tsx:54)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Adapter (at App.tsx:50)
in AppComponent (created by inject-AppComponent-with-mainStore)
in inject-AppComponent-with-mainStore (created by Context.Consumer)
in injectIntl(inject-AppComponent-with-mainStore) (created by Route)
in Route (at src/index.tsx:30)
in Router (created by HashRouter)
in HashRouter (at src/index.tsx:29)
in LocaleProvider (created by Context.Consumer)
in LocaleReceiver (created by ConfigProvider)
in ConfigProvider (created by _class)
in IntlProvider (created by _class)
in _class (created by Context.Consumer)
in Provider (created by Context.Consumer)
in CubaAppProvider (at src/index.tsx:24)
console. @ index.js:1437
index.js:1437 Warning: Failed prop type: Invalid prop value supplied to Option.
in Option (created by Select)
in Select (created by Context.Consumer)
in Select (created by _class)
in _class (created by inject-_class-with-mainStore)
in inject-_class-with-mainStore (created by _class)
in span (created by Context.Consumer)
in div (created by Context.Consumer)
in div (created by Context.Consumer)
in Col (created by Context.Consumer)
in div (created by Context.Consumer)
in Row (created by Context.Consumer)
in FormItem (created by _class)
in _class (created by inject-_class-with-mainStore)
in inject-_class-with-mainStore (created by _class)
in _class (created by inject-_class-with-mainStore)
in inject-_class-with-mainStore (at BeneficiaryBankAccountEditor.tsx:152)
in form (created by Context.Consumer)
in Form (at BeneficiaryBankAccountEditor.tsx:130)
in div (created by Context.Consumer)
in div (created by Context.Consumer)
in Card (at BeneficiaryBankAccountEditor.tsx:129)
in BeneficiaryBankAccountEditorComponent (created by Form(BeneficiaryBankAccountEditorComponent))
in Form(BeneficiaryBankAccountEditorComponent)
in Unknown (created by Context.Consumer)
in injectIntl(Component) (at BeneficiaryBankAccountPage.tsx:19)
in BeneficiaryBankAccountPage (created by Route)
in Route (at App.tsx:78)
in Switch (at App.tsx:75)
in main (created by Basic)
in Basic (created by Context.Consumer)
in Adapter (at App.tsx:74)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Adapter (at App.tsx:73)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Adapter (at App.tsx:54)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Adapter (at App.tsx:50)
in AppComponent (created by inject-AppComponent-with-mainStore)
in inject-AppComponent-with-mainStore (created by Context.Consumer)
in injectIntl(inject-AppComponent-with-mainStore) (created by Route)
in Route (at src/index.tsx:30)
in Router (created by HashRouter)
in HashRouter (at src/index.tsx:29)
in LocaleProvider (created by Context.Consumer)
in LocaleReceiver (created by ConfigProvider)
in ConfigProvider (created by _class)
in IntlProvider (created by _class)
in _class (created by Context.Consumer)
in Provider (created by Context.Consumer)
in CubaAppProvider (at src/index.tsx:24)
console. @ index.js:1437
react-dom.development.js:14816 Uncaught Error: Objects are not valid as a React child (found: object with keys {_entityName, _instanceName, id, bankCode, bankName}). If you meant to render a collection of children, use an array instead.
in div (created by Select)
in div (created by Select)
in div (created by Select)
in div (created by Select)
in Trigger (created by SelectTrigger)
in SelectTrigger (created by Select)
in Select (created by Context.Consumer)
in Select (created by _class)
in _class (created by inject-_class-with-mainStore)
in inject-_class-with-mainStore (created by _class)
in span (created by Context.Consumer)
in div (created by Context.Consumer)
in div (created by Context.Consumer)
in Col (created by Context.Consumer)
in div (created by Context.Consumer)
in Row (created by Context.Consumer)
in FormItem (created by _class)
in _class (created by inject-_class-with-mainStore)
in inject-_class-with-mainStore (created by _class)
in _class (created by inject-_class-with-mainStore)
in inject-_class-with-mainStore (at BeneficiaryBankAccountEditor.tsx:152)
in form (created by Context.Consumer)
in Form (at BeneficiaryBankAccountEditor.tsx:130)
in div (created by Context.Consumer)
in div (created by Context.Consumer)
in Card (at BeneficiaryBankAccountEditor.tsx:129)
in BeneficiaryBankAccountEditorComponent (created by Form(BeneficiaryBankAccountEditorComponent))
in Form(BeneficiaryBankAccountEditorComponent)
in Unknown (created by Context.Consumer)
in injectIntl(Component) (at BeneficiaryBankAccountPage.tsx:19)
in BeneficiaryBankAccountPage (created by Route)
in Route (at App.tsx:78)
in Switch (at App.tsx:75)
in main (created by Basic)
in Basic (created by Context.Consumer)
in Adapter (at App.tsx:74)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Adapter (at App.tsx:73)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Adapter (at App.tsx:54)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Adapter (at App.tsx:50)
in AppComponent (created by inject-AppComponent-with-mainStore)
in inject-AppComponent-with-mainStore (created by Context.Consumer)
in injectIntl(inject-AppComponent-with-mainStore) (created by Route)
in Route (at src/index.tsx:30)
in Router (created by HashRouter)
in HashRouter (at src/index.tsx:29)
in LocaleProvider (created by Context.Consumer)
in LocaleReceiver (created by ConfigProvider)
in ConfigProvider (created by _class)
in IntlProvider (created by _class)
in _class (created by Context.Consumer)
in Provider (created by Context.Consumer)
in CubaAppProvider (at src/index.tsx:24)
at throwOnInvalidObjectType (react-dom.development.js:14816)
at reconcileChildFibers (react-dom.development.js:15664)
at reconcileChildren (react-dom.development.js:18116)
at updateHostComponent (react-dom.development.js:18625)
at beginWork$1 (react-dom.development.js:20183)
at HTMLUnknownElement.callCallback (react-dom.development.js:337)
at Object.invokeGuardedCallbackDev (react-dom.development.js:386)
at invokeGuardedCallback (react-dom.development.js:439)
at beginWork$$1 (react-dom.development.js:25777)
at performUnitOfWork (react-dom.development.js:24691)
at workLoopSync (react-dom.development.js:24667)
at performSyncWorkOnRoot (react-dom.development.js:24256)
at react-dom.development.js:12286
at unstable_runWithPriority (scheduler.development.js:704)
at runWithPriority$2 (react-dom.development.js:12232)
at flushSyncCallbackQueueImpl (react-dom.development.js:12281)
at flushSyncCallbackQueue (react-dom.development.js:12269)
at batchedUpdates$1 (react-dom.development.js:24377)
at reactionScheduler (mobx.module.js:2223)
at runReactions (mobx.module.js:2191)
at endBatch (mobx.module.js:1826)
at _endAction (mobx.module.js:1129)
at executeAction (mobx.module.js:1076)
at runInAction (mobx.module.js:2389)
at index.esm.js:988
index.js:1437 The above error occurred in the

component:
in div (created by Select)
in div (created by Select)
in div (created by Select)
in div (created by Select)
in Trigger (created by SelectTrigger)
in SelectTrigger (created by Select)
in Select (created by Context.Consumer)
in Select (created by _class)
in _class (created by inject-_class-with-mainStore)
in inject-_class-with-mainStore (created by _class)
in span (created by Context.Consumer)
in div (created by Context.Consumer)
in div (created by Context.Consumer)
in Col (created by Context.Consumer)
in div (created by Context.Consumer)
in Row (created by Context.Consumer)
in FormItem (created by _class)
in _class (created by inject-_class-with-mainStore)
in inject-_class-with-mainStore (created by _class)
in _class (created by inject-_class-with-mainStore)
in inject-_class-with-mainStore (at BeneficiaryBankAccountEditor.tsx:152)
in form (created by Context.Consumer)
in Form (at BeneficiaryBankAccountEditor.tsx:130)
in div (created by Context.Consumer)
in div (created by Context.Consumer)
in Card (at BeneficiaryBankAccountEditor.tsx:129)
in BeneficiaryBankAccountEditorComponent (created by Form(BeneficiaryBankAccountEditorComponent))
in Form(BeneficiaryBankAccountEditorComponent)
in Unknown (created by Context.Consumer)
in injectIntl(Component) (at BeneficiaryBankAccountPage.tsx:19)
in BeneficiaryBankAccountPage (created by Route)
in Route (at App.tsx:78)
in Switch (at App.tsx:75)
in main (created by Basic)
in Basic (created by Context.Consumer)
in Adapter (at App.tsx:74)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Adapter (at App.tsx:73)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Adapter (at App.tsx:54)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Adapter (at App.tsx:50)
in AppComponent (created by inject-AppComponent-with-mainStore)
in inject-AppComponent-with-mainStore (created by Context.Consumer)
in injectIntl(inject-AppComponent-with-mainStore) (created by Route)
in Route (at src/index.tsx:30)
in Router (created by HashRouter)
in HashRouter (at src/index.tsx:29)
in LocaleProvider (created by Context.Consumer)
in LocaleReceiver (created by ConfigProvider)
in ConfigProvider (created by _class)
in IntlProvider (created by _class)
in _class (created by Context.Consumer)
in Provider (created by Context.Consumer)
in CubaAppProvider (at src/index.tsx:24)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://fb.me/react-error-boundaries to learn more about error boundaries.
console. @ index.js:1437

The following is the Editor code that is generated by the Cuba Frontend Generator:
import * as React from “react”;

import { FormEvent } from "react";

import { Alert, Button, Card, Form, message } from "antd";

import { observer } from "mobx-react";

import { BeneficiaryBankAccountPage } from "./BeneficiaryBankAccountPage";

import { FormComponentProps } from "antd/lib/form";

import { Link, Redirect } from "react-router-dom";

import { IReactionDisposer, observable, reaction, toJS } from "mobx";

import {

  FormattedMessage,

  injectIntl,

  WrappedComponentProps

} from "react-intl";

import {

  collection,

  Field,

  instance,

  withLocalizedForm,

  extractServerValidationErrors,

  constructFieldsWithErrors,

  clearFieldErrors,

  MultilineText, 

} from "@cuba-platform/react";

import "../../app/App.css";

import { BeneficiaryBankAccount } from "../../cuba/entities/fundraiser_BeneficiaryBankAccount";

import { Beneficiary } from "../../cuba/entities/fundraiser_Beneficiary";

import { Bank } from "../../cuba/entities/fundraiser_Bank";

type Props = FormComponentProps & EditorProps;

type EditorProps = {

  entityId: string;

};

@observer

class BeneficiaryBankAccountEditorComponent extends React.Component<

  Props & WrappedComponentProps

> {

  dataInstance = instance<BeneficiaryBankAccount>(BeneficiaryBankAccount.NAME, {

    view: "beneficiaryBankAccount-view-relations",

    loadImmediately: false

  });

  beneficiarysDc = collection<Beneficiary>(Beneficiary.NAME, {

    view: "_mimimal"

  });

  bankDc = collection<Bank>(Bank.NAME, {

    view: "_minimal",

    sort: "bankCode"

  });

  @observable

  updated = false;

  reactionDisposer: IReactionDisposer;

  fields = ["bankAccountNumber", "beneficiary", "bank", "groupId"];

  @observable

  globalErrors: string[] = [];

  handleSubmit = (e: FormEvent) => {

    e.preventDefault();

    this.props.form.validateFields((err, values) => {

      if (err) {

        message.error(

          this.props.intl.formatMessage({

            id: "management.editor.validationError"

          })

        );

        return;

      }

      this.dataInstance

        .update(this.props.form.getFieldsValue(this.fields))

        .then(() => {

          message.success(

            this.props.intl.formatMessage({ id: "management.editor.success" })

          );

          this.updated = true;

        })

        .catch((e: any) => {

          if (e.response && typeof e.response.json === "function") {

            e.response.json().then((response: any) => {

              clearFieldErrors(this.props.form);

              const {

                globalErrors,

                fieldErrors

              } = extractServerValidationErrors(response);

              this.globalErrors = globalErrors;

              if (fieldErrors.size > 0) {

                this.props.form.setFields(

                  constructFieldsWithErrors(fieldErrors, this.props.form)

                );

              }

              if (fieldErrors.size > 0 || globalErrors.length > 0) {

                message.error(

                  this.props.intl.formatMessage({

                    id: "management.editor.validationError"

                  })

                );

              } else {

                message.error(

                  this.props.intl.formatMessage({

                    id: "management.editor.error"

                  })

                );

              }

            });

          } else {

            message.error(

              this.props.intl.formatMessage({ id: "management.editor.error" })

            );

          }

        });

    });

  };

  render() {

    if (this.updated) {

      return <Redirect to={BeneficiaryBankAccountPage.PATH} />;

    }

    const { status } = this.dataInstance;

    return (  

      <Card className="narrow-layout">

        <Form onSubmit={this.handleSubmit} layout="vertical">

          <Field

            entityName={BeneficiaryBankAccount.NAME}

            propertyName="bankAccountNumber"

            form={this.props.form}

            formItemOpts={{ style: { marginBottom: "12px" } }}

            getFieldDecoratorOpts={{

              rules: [{ required: true }]

            }}

          />

          <Field

            entityName={BeneficiaryBankAccount.NAME}

            propertyName="beneficiary"

            form={this.props.form}

            formItemOpts={{ style: { marginBottom: "12px" } }}

            optionsContainer={this.beneficiarysDc}

            getFieldDecoratorOpts={{

              rules: [{ required: true }]

            }}

          />

          <Field

            entityName={BeneficiaryBankAccount.NAME}

            propertyName="bank"

            form={this.props.form}

            formItemOpts={{ style: { marginBottom: "12px" } }}

            optionsContainer={this.bankDc}

            getFieldDecoratorOpts={{

              rules: [{ required: true }]

            }}

          />

          <Field

            entityName={BeneficiaryBankAccount.NAME}

            propertyName="groupId"

            form={this.props.form}

            formItemOpts={{ style: { marginBottom: "12px" } }}

            getFieldDecoratorOpts={{

              rules: [{ required: true }]

            }}

          />

          {this.globalErrors.length > 0 && (

            <Alert

              message={<MultilineText lines={toJS(this.globalErrors)} />}

              type="error"

              style={{ marginBottom: "24px" }}

            />

          )}

          <Form.Item style={{ textAlign: "center" }}>

            <Link to={BeneficiaryBankAccountPage.PATH}>

              <Button htmlType="button">

                <FormattedMessage id="management.editor.cancel" />

              </Button>

            </Link>

            <Button

              type="primary"

              htmlType="submit"

              disabled={status !== "DONE" && status !== "ERROR"}

              loading={status === "LOADING"}

              style={{ marginLeft: "8px" }}

            >

              <FormattedMessage id="management.editor.submit" />

            </Button>

          </Form.Item>

        </Form>

      </Card>

    );

  }

  componentDidMount() {

    if (this.props.entityId !== BeneficiaryBankAccountPage.NEW_SUBPATH) {

      this.dataInstance.load(this.props.entityId);

    } else {

      this.dataInstance.setItem(new BeneficiaryBankAccount());

    }

    this.reactionDisposer = reaction(

      () => {

        return this.dataInstance.item;

      },

      () => {

        this.props.form.setFieldsValue(

          this.dataInstance.getFieldValues(this.fields)

        );

      }

    );

  }

  componentWillUnmount() {

    this.reactionDisposer();

  }

}

export default injectIntl(

  withLocalizedForm<EditorProps>({

    onValuesChange: (props: any, changedValues: any) => {

      // Reset server-side errors when field is edited

      Object.keys(changedValues).forEach((fieldName: string) => {

        props.form.setFields({

          [fieldName]: {

            value: changedValues[fieldName]

          }

        });

      });

    }

  })(BeneficiaryBankAccountEditorComponent)

);

The following is the CUBA Entity:
package com.fundraiser.entity;

import com.haulmont.chile.core.annotations.Composition;
import com.haulmont.chile.core.annotations.NamePattern;
import com.haulmont.cuba.core.entity.StandardEntity;
import com.haulmont.cuba.core.entity.annotation.OnDelete;
import com.haulmont.cuba.core.global.DeletePolicy;
import com.haulmont.cuba.security.entity.Group;
import org.hibernate.validator.constraints.Length;

import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;

@NamePattern("%s|bankAccountNumber")
@Table(name = "FUNDRAISER_BENEFICIARY_BANK_ACCOUNT")
@Entity(name = "fundraiser_BeneficiaryBankAccount")
public class BeneficiaryBankAccount extends StandardEntity {
    private static final long serialVersionUID = -4769306480790398843L;

    @NotNull
    @OnDelete(DeletePolicy.CASCADE)
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "BENEFICIARY_ID")
    protected Beneficiary beneficiary;

    @Pattern(message = "Only numeric value is allowed", regexp = "^[0-9]*$")
    @NotBlank
    @NotEmpty
    @Length(min = 1, max = 20)
    @NotNull
    @Column(name = "BANK_ACCOUNT_NUMBER", nullable = false, length = 20)
    protected String bankAccountNumber;

    @NotNull
    @Composition
    @OnDelete(DeletePolicy.UNLINK)
    @OneToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "BANK_ID")
    protected Bank bank;

    @NotNull
    @Composition
    @OnDelete(DeletePolicy.UNLINK)
    @OneToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "GROUP_ID_ID")
    protected Group groupId;

    public Beneficiary getBeneficiary() {
        return beneficiary;
    }

    public void setBeneficiary(Beneficiary beneficiary) {
        this.beneficiary = beneficiary;
    }

    public Group getGroupId() {
        return groupId;
    }

    public void setGroupId(Group groupId) {
        this.groupId = groupId;
    }

    public Bank getBank() {
        return bank;
    }

    public void setBank(Bank bank) {
        this.bank = bank;
    }

    public String getBankAccountNumber() {
        return bankAccountNumber;
    }

    public void setBankAccountNumber(String bankAccountNumber) {
        this.bankAccountNumber = bankAccountNumber;
    }
}

I think that the issue is related to the Entity Relations.

Can anyone advise why the code generated by the Cuba Frontend Generator is not working properly? Is there something that I’m missing that I would need to edit the code manually in order to display the Relations? Why is the entity relations not working with the Cuba Component ?

Thank you in advance

Hi, it caused by incorrect handling of composition in Field component, will be fixed in the next bugfix release.
As a workaround you can remove <Field>s displaying compositions and handle such relations manually (as full composition support comes in the next feature release)

Thanks for the info.
When is the next feature release?

It’s planned for February.