Frappe Client ScriptJavascript

How to Conditionally Remove Rows from a Child Table in Frappe

Remove table row

Learn to write a Frappe Client Script to programmatically remove rows from a child table based on field values using grid.grid_rows[i].remove().

In Frappe and ERPNext, managing child table data dynamically is a common requirement for creating interactive and user-friendly forms. This code snippet demonstrates a powerful technique for removing rows from a child table based on specific conditions, triggered by a custom button.

This client-side script is particularly useful for cleaning up data, filtering entries based on a parent document's state, or allowing users to perform bulk actions on child table rows without needing to manually delete them one by one.

quotation_client_script.jsjavascript
1// On click event of remove_btn
2
3frappe.ui.form.on("Quotation", "remove_btn", function(frm) {
4 var tbl = frm.doc.items || [];
5 var i = tbl.length;
6 while (i--){
7 if(frm.doc.select_item){
8 if(tbl[i].main_item == frm.doc.select_item){
9 frm.get_field("items").grid.grid_rows[i].remove();
10 }
11 }
12 else {
13 if(tbl[i].main_item){
14 frm.get_field("items").grid.grid_rows[i].remove();
15 }
16 }
17 }
18 cur_frm.refresh();
19
20});

Understanding This Code

What It Does

This script iterates through all rows in the 'items' child table and removes those that match a specific condition related to a value in the parent DocType.

When To Use

Use this script when you need to provide users with a way to perform a bulk removal of child table rows based on criteria defined in the main form, such as filtering by a selected item category.

Prerequisites

  • A parent DocType (e.g., 'Quotation') with a child table (e.g., 'items').
  • A custom button with fieldname 'remove_btn' on the parent DocType.
  • A field in the parent form (e.g., 'select_item') to use as a filter condition.

Key Concepts

Important ideas to understand in this code

frappe.ui.form.on

A client-side API used to attach event handlers to form events. In this case, it listens for a click on the field (button) named 'remove_btn' on the 'Quotation' form.

Learn more

Child Table Grid API

The expression 'frm.get_field("items").grid.grid_rows' accesses the live grid rows of the child table. The .remove() method on a specific grid row object deletes it from both the UI and the form's data.

Learn more

Iterating Backwards with while(i--)

When removing items from an array or collection you are iterating over, it's crucial to loop backward. This prevents issues with shifting indices that would cause the loop to skip items if you were iterating forward.

Learn more

cur_frm.refresh()

After modifying the grid, calling cur_frm.refresh() is essential. It redraws the form, including the child table, ensuring that UI calculations, totals, and layout are updated correctly.

Learn more

Step-by-Step Tutorial

Follow along to understand how this code works

1

Add a Custom Button and Filter Field

In your ERPNext instance, go to 'Customize Form' and select the 'Quotation' DocType. Add a new field of type 'Button' and set its 'Fieldname' to 'remove_btn'. Add another field (e.g., a Link field named 'select_item') that will be used for filtering.

bash
// No code needed for this step. Use the Form Customization UI.
Next Step
2

Create a New Client Script

Navigate to 'Client Script' from the Awesome Bar. Click 'New', select 'Quotation' as the DocType, and save the script.

bash
// No code needed for this step. Use the Client Script creation UI.
Next Step
3

Implement the Row Removal Logic

Copy and paste the provided JavaScript code into the script editor. This code binds the row removal logic to the click event of your new button.

javascript
frappe.ui.form.on("Quotation", "remove_btn", function(frm) {
    var tbl = frm.doc.items || [];
    var i = tbl.length;
    while (i--){
        if(frm.doc.select_item){		
            if(tbl[i].main_item == frm.doc.select_item){
                frm.get_field("items").grid.grid_rows[i].remove();
            }	
        }
        else {
            // If no item is selected, remove all rows with a main_item
            if(tbl[i].main_item){
                frm.get_field("items").grid.grid_rows[i].remove();
            }
        }
    }
    cur_frm.refresh();
});
Next Step
4

Test the Functionality

Open an existing Quotation or create a new one. Add several items to the 'items' table. Select a specific item in the 'select_item' field and click your 'Remove' button. Only the matching rows should be deleted from the child table.

bash
// No code for this step. Test the functionality within a Quotation form.

Common Issues & Solutions

Troubleshoot problems you might encounter