Frappe Client ScriptJavascript

How to Trigger a Client Script on Child Table Row Deletion in Frappe?

Trigger on Row deletion

Learn how to execute a Frappe Client Script when a user adds or removes a row from a Child Table using the 'fieldname_add' and 'fieldname_remove' triggers.

In Frappe application development, a common requirement is to dynamically respond to user actions within child tables (also known as Table fields). This could involve recalculating totals, validating data, or triggering other logic when a row is added, moved, or deleted.

Frappe's client-side API provides a powerful and specific set of triggers for these events. By attaching an event handler to the Child DocType, you can precisely control the form's behavior, ensuring data integrity and a responsive user experience.

1// Assuming your parent DocType is 'Item' and has a Table Field 'color' linked to the 'Item Color' DocType.
2// The client script MUST be for the CHILD DocType: 'Item Color'.
3
4frappe.ui.form.on('Item Color', {
5 // This trigger fires AFTER a row is removed.
6 color_remove: function(frm) {
7 // 'frm' is the parent form object (Item form).
8 // You can access the remaining rows via frm.doc.color
9 console.log('A row was removed. Remaining rows:', frm.doc.color);
10 frappe.msgprint('A color row was removed.');
11 // Your custom logic, like recalculating totals, can go here.
12 },
13 // This trigger fires AFTER a new row is added.
14 color_add: function(frm, cdt, cdn) {
15 // 'cdt' is the child doctype name ('Item Color')
16 // 'cdn' is the child doc name (the unique ID of the new row)
17 console.log('A new row was added:', cdn);
18 frappe.msgprint('New color row added!');
19 // You can get the new row's data via locals
20 let new_row = locals[cdt][cdn];
21 console.log('New row data:', new_row);
22 }
23});

Understanding This Code

What It Does

This script attaches event handlers to a Child Table within a Frappe form. It allows developers to execute custom Javascript code precisely when a row is added or removed by the user.

When To Use

Use this pattern when you need to perform actions in response to changes in a child table. Common use cases include recalculating totals in the parent DocType, validating entries, or triggering API calls based on the child table's contents.

Prerequisites

  • A Parent DocType with a Table field (Child Table).
  • Basic knowledge of Frappe Client Scripting.

Key Concepts

Important ideas to understand in this code

frappe.ui.form.on Event Handler

The primary function for attaching client-side event listeners in Frappe. It targets a specific DocType and an event name. For child tables, the event is scoped to the Child DocType's name.

Learn more

Child Table Triggers (fieldname_add / fieldname_remove)

These are special triggers fired on the Child DocType's form event handler. 'fieldname' must be replaced with the actual fieldname of the Table field in the Parent DocType. These events give you access to the parent form object ('frm') at the moment of the action.

Learn more

The Form Object ('frm')

Within the child table trigger, the 'frm' object is the controller for the PARENT form view. It provides access to the parent document's data (frm.doc), metadata, and methods to interact with the UI, like frm.refresh_field() or frm.set_value().

Learn more

Step-by-Step Tutorial

Follow along to understand how this code works

1

Identify Your DocTypes and Fieldnames

First, identify your Parent DocType, Child DocType, and the fieldname of the child table in the Parent DocType. For our example, Parent is 'Sales Invoice', Child is 'Sales Invoice Item', and the table fieldname is 'items'.

javascript
// Parent DocType: Sales Invoice
// Child DocType: Sales Invoice Item
// Table Fieldname in Sales Invoice: 'items'
Next Step
2

Create a New Client Script for the Child DocType

Navigate to 'Client Script' in your Frappe desk and create a new script. Set 'Select DocType' to the *Child DocType*, which is 'Sales Invoice Item' in this case. This is a crucial step; the event handler must be attached to the Child DocType.

javascript
// In Frappe Desk:
// 1. Go to Client Script List
// 2. Click 'New'
// 3. Select DocType: 'Sales Invoice Item'
Next Step
3

Implement the 'items_remove' Trigger

In the script, use 'frappe.ui.form.on' for the 'Sales Invoice Item' DocType. The event name will be 'items_remove', where 'items' is the fieldname. Inside the function, you can write your logic. The 'frm' object here refers to the parent form ('Sales Invoice').

javascript
frappe.ui.form.on('Sales Invoice Item', {
    items_remove: function(frm) {
        console.log('An item was removed!');
        // The 'frm' object is the parent form (Sales Invoice)
        // Example: Recalculate total
        let total = 0;
        (frm.doc.items || []).forEach(item => {
            total += item.amount;
        });
        frm.set_value('grand_total', total);
        frm.refresh_field('grand_total');
    }
});
Next Step
4

Implement the 'before_items_remove' Trigger (Optional)

If you need to access the data of the row *before* it gets deleted or want to prevent deletion, use the 'before_fieldname_remove' trigger. This trigger receives 'cdt' and 'cdn' arguments.

javascript
frappe.ui.form.on('Sales Invoice Item', {
    before_items_remove: function(frm, cdt, cdn) {
        let row = frappe.get_doc(cdt, cdn);
        if (row.is_billed) {
            frappe.throw('Cannot remove an item that has already been billed.');
        }
    }
});

Common Issues & Solutions

Troubleshoot problems you might encounter