How to Trigger a Client Script on Child Table Row Deletion in Frappe?
Trigger on Row deletionLearn 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
4 frappe.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 moreChild 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 moreThe 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 moreStep-by-Step Tutorial
Follow along to understand how this code works
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'.
// Parent DocType: Sales Invoice
// Child DocType: Sales Invoice Item
// Table Fieldname in Sales Invoice: 'items'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.
// In Frappe Desk:
// 1. Go to Client Script List
// 2. Click 'New'
// 3. Select DocType: 'Sales Invoice Item'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').
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');
}
});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.
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