> ## Documentation Index
> Fetch the complete documentation index at: https://help.dingtalk.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Subform

> This article introduces the concept, use cases, basic features, and advanced features of subforms, including the upgraded settings panel, optimized action button order, and the collapse/expand feature in tiled mode. It also covers batch import and export of subforms, along with answers to frequently asked questions.

## 1. What Is a Subform

An advanced container component that lets you add Text, Numerical, Date, and other components inside it. For example, use a subform when you need to add multiple product entries during product data entry.

## 2. Use Cases

Subforms are commonly used for data entry scenarios such as outbound orders, inbound orders, and sales orders. Product details in these scenarios can be recorded in a subform, and you can add rows as needed based on the data to enter.

<Note>
  For component attributes, usage, and examples, see [**this reference**](/open/yida/components/interface).
</Note>

## 3. Basic Features

| **Upgrade item**                                                                                            | **Before**                                                                        | **After**                                                                                                                                                                                                                                                                                                       |
| ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Settings panel                                                                                              | Settings are mixed together with no categorization, making them hard to find.     | Settings are grouped by subform configuration logic for easy discovery.<br />Based on frequent user feedback, additional configuration options have been added.                                                                                                                                                 |
| Subform action button order (edit mode)                                                                     | Newly added actions are shown first, and there is no Copy button.                 | Once enabled, the "Copy" and "Delete" buttons become the primary buttons at the front, followed by user-defined action buttons, and finally the "Move up/down" buttons (shown when enabled).<br />In edit mode, all buttons are shown by default.<br />**Note: The Copy button has not been fully rolled out.** |
| Subform action button order (submit mode)                                                                   | Copy action is not supported, and sort buttons are shown by default.              | Initial state (only one record): Delete is not available.                                                                                                                                                                                                                                                       |
| Collapse/expand added to tiled mode                                                                         | All records are displayed. If there are more than 20 records, they are paginated. | In tiled mode, click the icon outlined in red below to toggle expand/collapse.                                                                                                                                                                                                                                  |
| Freeze columns (spreadsheet mode)<br />**Currently, only freezing the leading columns on PC is supported.** | Freeze column is not supported.                                                   | Custom column freezing is supported, with up to 5 columns.<br />**The row number counts as 1 column.**<br />Default: none                                                                                                                                                                                       |
| Custom row number name (tiled mode)                                                                         | No setting for the row number name.                                               | The row number name can be customized.                                                                                                                                                                                                                                                                          |

### 3.1 Layout Mode

Set the layout of the subform. Spreadsheet mode is used by default.

#### 3.1.1 Spreadsheet Mode

Spreadsheet

Display effect:

**Note**: Subject and header display are only available on PC when the layout mode is Spreadsheet.

#### 3.1.2 Tiled Mode

Tiled

Effect:

### 3.2 Set the Add Button Name

The Add button is named "Add one" by default. You can customize the name. Example:

Set the Add button name

### 3.3 Set the Delete Button Name

Set the Delete button name

### 3.4 Maximum Number of Records That Can Be Added

| **Feature**                                              | **Free plan** | **Basic edition** | **Professional edition** | **Dedicated edition** |
| -------------------------------------------------------- | ------------- | ----------------- | ------------------------ | --------------------- |
| Maximum number of records that can be added to a subform | 50            | 500               | 500                      | 500                   |

Maximum number of subform records

### 3.5 Subform Column Width

Column width is customizable, so even long content displays properly.

Customize subform column width

### 3.6 Batch Import

Path: Open a form and you will see a Batch import button under the subform. Click Batch import to proceed. (If the form has public access (silent login) enabled, subforms within the form do not support batch import.)

**The field order in the subform batch import template matches the field order in the form design.**

Access page

* Efficient subform data entry: supports batch import via Excel.

Batch import page

After a successful import:

### 3.7 Batch Export

* Quickly export subform data:

Batch export

Batch export in progress

Batch export succeeded

### 3.8 Export Main Form and Subform Content Together to Excel

* Supports exporting main form data and subform data together.

* New export capabilities:

Form page

Source records page

Excel spreadsheet page

## 4. Advanced Features

### 4.1 Action Column

#### 4.1.1 Create an Action Column

After you create an action column, click Edit to bind actions.

Action column

Effect:

#### 4.1.2 Callback Function

See [**Action settings**](/yida/form/snftao).

Action - Item-0

Callback function

JS panel

#### 4.1.3 Custom Rendering

JSX is supported.

Custom rendering - bind action

Effect:

## 5. FAQ

<AccordionGroup>
  <Accordion id="q1" title="5.1 How do I batch delete records in a subform?">
    <Check>
      A subform component must retain at least one record.
    </Check>

    Configure a Delete action, as shown below:

    **Note**: By default, this action deletes all records. Batch deletion by selection is not yet supported.
  </Accordion>

  <Accordion id="q2" title="5.2 How do I get the value of a specific component inside a subform?">
    Use this approach when you want to obtain subform data for form validation or as input parameters for a data source call.

    **Sample code:**

    ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    const subFormInst = this.$('subform unique identifier');
    // First, get the row identifiers
    const items = subFormInst.getItems(); //  ["tfitem_1", "tfitem_2"]
    items.forEach(item => {
      const fieldInst = subFormInst.getComponent(item, 'unique identifier of the component inside the subform');
      console.log(fieldInst.getValue());
    });
    ```
  </Accordion>

  <Accordion id="q3" title="5.3 Subform associated with a subform & subform assignment">
    Commonly used for cross-app subform data association and for initializing subform data when the form loads.

    **Reference:** [**Detail-to-detail association**](/open/yida/components/form/tableField)
  </Accordion>

  <Accordion id="q4" title="5.4 Subform data export & report analysis on subform data">
    Because subform data cannot be displayed, analyzed, or exported on the data management page, display and export subform data in a spreadsheet on the report page instead.

    **Reference:** **Report analysis on subform data**
  </Accordion>

  <Accordion id="q5" title="5.5 If I set up a redirect function in a subform, can the button that runs the function be hidden after being clicked once?">
    Add the following code in Custom rendering:

    ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    export function onActionRender({ index, groupId, itemValue, actionKey }) {
     return <div id="anniu" onClick={()=>{
     document.getElementById('anniu').style.display = 'none'
     }}>Action button name</div>;
    }
    ```
  </Accordion>

  <Accordion id="q6" title="5.6 How do I set up the move up/down buttons for a subform?">
    This is a hidden feature. Go to the form's edit page and enable "Show sort" for the subform. The move up/down buttons then appear, as shown below:
  </Accordion>

  <Accordion id="q7" title="5.7 Can I validate the uniqueness of one row of text in a Multi-line text component within a subform?">
    Configure a custom validation function on the component inside the subform, iterate over all records in the subform, and compare them with the current record.
  </Accordion>

  <Accordion id="q8" title="5.8 How do I automatically get the total number of records in a subform?">
    (1) Use one subform component and one Numerical component, and set the Default value of the Numerical component to 1, as shown:

    (2) Click Subform > Premium > Create action. Create two actions: one for onAddClick and one for onDelClick. Write the code when creating the actions, as shown:

    The following code can be used directly. **Note**: Enter the unique identifier of the Numerical component in the first parentheses, and the unique identifier of the subform in the second parentheses. Refer to step (3) for the setup.

    ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    this.$('Numerical component identifier').setValue(this.$('Subform identifier').getValue().length);
    ```

    (3) First, create an onAddClick action, as shown:

    Then paste the code above:

    (4) Create an onDelClick action and paste the same code:

    Then paste the code above:

    After clicking Save, the result is:

    When you delete a newly added record, the count decreases automatically:
  </Accordion>

  <Accordion id="q9" title="5.9 How do I control the number of subform records with a variable?">
    (1) Drag in a Numerical component and bind the "onChange" action to it.

    (2) Copy the following code into the JS panel:

    ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    export function onChange({ value }) {
      console.log('onChange', value);
      let a = [];
      for(let i=0;i<value;i++){
        let b= {
          "textField_kurr4i2o" : "",   // Unique identifier of another component inside the subform
        }
        a.push(b)
      }
      this.$("tableField_kurr4i2m").setValue(a)   // Unique identifier of the subform component
    }
    ```

    (3) Result:
  </Accordion>
</AccordionGroup>

### 5.10 A Hidden Component in a Subform Returns Null

When processing a subform, the `value` of a hidden attribute is reset. It is configured as null, so assigning a hidden-state subform to another subform returns null.

<AccordionGroup>
  <Accordion id="q10" title="5.11 How do I control the show/hide state of subform components based on the value of a main form field?">
    To control the state of a subform component based on the value of a main form field, follow these steps:

    **Create a variable dp2**

    **Bind the state of a Single-line text field in the subform to the variable state.dp2**

    **Bind an action on the main form Select dropdown to assign the state to variable dp2**

    Sample code:

    ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    export function onChange({value, actionType, item}) {
      console.log('onChange', value);
      if(value=="Option 1"){
        this.setState({ dp2: "DISABLED"})
      }
      else{
        this.setState({ dp2: "HIDDEN" })
      }
    ```
  </Accordion>

  <Accordion id="q11" title="5.12 How do I populate a Multiselect member component in the main form into multiple records in a subform?">
    Bind an action on the main form member component to perform the assignment, as shown below:

    **Sample code:**

    ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    export function onChange({ value }) {
      if (value && value.length > 0) {
        let newValue = value.map((item) => {
          return {
            "Unique identifier of the subform member component": { label: item.label, value: item.value }
          }
        })
        // Assign the value to the detail component on the current page
        this.$("Unique identifier of the subform component").setValue(newValue);
      } else {
        this.$("Unique identifier of the subform component").reset();
      }
    }
    ```
  </Accordion>

  <Accordion id="q12" title="5.13 How many component fields can be placed in a single subform?">
    You cannot insert a subform component inside a subform, but you can add other regular components. The Free plan supports up to 20; other editions support up to 100. Exceeding this limit degrades load performance. We recommend splitting the business logic.
  </Accordion>
</AccordionGroup>

### 5.11 How Do I Get Subform Data

Use the common component API `getValue` to get subform data.

**Sample code:**

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
this.$('subform component unique identifier').getValue(); // Get subform data
```

**Response example:**

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
[
  {
    textField_lzjl01sh: 'Single-line text 1',
    numberField_lzjl01si: 1,
  }, {
    textField_lzjl01sh: 'Single-line text 2',
    numberField_lzjl01si: 2,
  }, {
    textField_lzjl01sh: 'Single-line text 3',
    numberField_lzjl01si: 3,
  }
]
```

### 5.12 How Do I Get Subform Row Identifiers

Use the subform API `getItems` to get the subform row identifiers.

**Sample code:**

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
this.$('subform component unique identifier').getItems(); // Get subform row identifiers
```

**Response example:**

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
['tfitem_1', 'tfitem_2', 'tfitem_3']
```

### 5.13 How Do I Get the Data of a Specific Row in a Subform

Use the subform API `getItemValue` to get the data of a specific row in the subform.

**Sample code:**

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
this.$('subform component unique identifier').getItemValue('row identifier of the specified row'); // Get data of the specified row
```

**Response example:**

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  textField_lzjl01sh: 'Single-line text 2',
  numberField_lzjl01si: 2,
}
```

### 5.14 How Do I Get the Currently Changed Field When a Subform's onChange Fires

When subform data changes, the subform's onChange event is triggered not only by cell edits but also by formula calculations, data linkage, or external assignments. To identify the change, use the `extra` parameter of the subform's onChange event to get the currently changed field, and the `changes` parameter to get its value.

**Sample code:**

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
// Subform onChange
export function onTableChange({ value, extra }) {
  const { fieldId, changes = {} } = extra || {};
  // Check whether the item name field has changed
  if (fieldId && fieldId === 'unique identifier of the item name field in the subform') {
    // Add the logic to execute after the field changes here
    console.log(changes.value);
  }
}
```

**Result:**

### 5.15 How Do I Update Other Fields in the Current Row When a Component Inside the Subform Changes

Use the subform API `updateItemValue` to update other fields in the current row of the subform.

<Note>
  Note: This API can only be used in subform component events, not in events of components inside the subform.
</Note>

**Sample code:**

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
// Subform onChange
export function onTableChange({ value, extra }) {
  const { formGroupId, from, tableFieldId, fieldId, changes = {} } = extra || {};
  // Required to prevent an infinite loop caused by triggering onChange again after using updateItemValue to update subform data
  if (from === 'setItemValue') { return; }
  const tableField = this.$(tableFieldId); // Get the subform definition
  // Check whether the item name field has changed
  if (fieldId && fieldId === 'unique identifier of the item name field in the subform') {
    tableField.updateItemValue(formGroupId, {
      'unique identifier of another component in the current row': 'updated value',
    }); // Update the values of other components in the current row
  }
}
```

**Result:**

After you enter the item name, the value of **Item name** is assigned to **Item name copy** in the current row.
