Skip to main content

Frappe Form API Guide (v15)

What is the Frappe Form API?

The Frappe Form API provides a client-side programming interface to interact with documents displayed on a form in Desk. It allows developers to respond to form events, change values, refresh fields, call server methods, and manage UI behavior for DocTypes in ERPNext using JavaScript.

The Form API runs in the browser and is typically used inside:

  • *.js DocType client scripts
  • Custom Script records
  • App-level form controllers
  • Override scripts

When should you use the Form API?

Use the Form API when:

  • You need to handle form lifecycle events (onload, refresh, etc.)
  • You want to manipulate field values dynamically
  • You are adding validation logic on client-side
  • You modify child table rows
  • You trigger server logic from UI
  • You hide/show fields dynamically
  • You need to update UI behavior during user interaction

How does the Frappe Form API work?

The Form API exposes a global object called frappe.ui.form, which is accessible inside DocType JavaScript controllers via the parameter frm.

A typical controller looks like:

frappe.ui.form.on('Sales Invoice', {
refresh(frm) {
// custom logic here
}
});

The frm object represents the current form instance, including:

  • loaded document (frm.doc)
  • metadata
  • field definitions
  • form events
  • UI interaction functions
  • API for child tables

Core Concepts in Form API

The Form API provides several categories of functions:

Category Purpose
Form Events Lifecycle hooks
Field Operations Set/get field values
Field Refresh Reload UI components
Server Calls Invoke Python whitelisted methods
UI Modifications Control buttons and layout
Child Table API Manage table rows
Validation Prevent invalid submissions

Form Lifecycle Events

What are form events?

Form events allow you to execute custom code at specific points in the form lifecycle.

Common events include:

Event Trigger
onload Form loaded first time
refresh Every time the form loads
validate Before saving
before_save Just before save request
after_save After save completes
submit When document is submitted
cancel When canceled

Example:

frappe.ui.form.on('Customer', {
refresh(frm) {
console.log("Form refreshed");
}
});

Accessing the Document

How do you access the form document?

Use frm.doc to read and update the current document.

Example:

let name = frm.doc.customer_name;

Update a field value:

frm.set_value('customer_name', 'John Doe');

Updating Field Values

How do you update a field from JavaScript?

Use the set_value function:

frm.set_value('status', 'Active');

To update multiple fields at once:

frm.set_value({
status: 'Active',
customer_group: 'Retail'
});

Refreshing Fields

After updating values, you may need to refresh the UI:

frm.refresh_field('status');

Refresh all fields:

frm.refresh();

Calling Python from Form API

How do you call a server method?

Use frappe.call() from JavaScript to execute a whitelisted Python function.

Python (whitelist)

@frappe.whitelist()
def compute_total(amount, qty):
return amount * qty

JavaScript

frappe.call({
method: 'myapp.api.compute_total',
args: {
amount: 100,
qty: 2
},
callback(r) {
frm.set_value('total', r.message);
}
});

Adding Custom Buttons

How do you add a button to the form?

Use the add_custom_button method inside refresh():

frm.add_custom_button('Compute', () => {
frappe.msgprint("Your logic here!");
});

Place button inside a group:

frm.add_custom_button('Compute', () => {}, 'Actions');

UI Controls

Hide or show fields

Hide:

frm.toggle_display('field_name', false);

Show:

frm.toggle_display('field_name', true);

Working With Child Tables

How do you add a row to a child table?

let row = frm.add_child('items', {
item_code: 'ITEM-0001',
qty: 5
});
frm.refresh_field('items');

Remove row:

frm.get_field('items').grid.remove(row);
frm.refresh_field('items');

Loop through rows:

frm.doc.items.forEach(row => {
console.log(row.item_code);
});

Client-side Validation

How do you stop the form from saving?

Use validation events:

frappe.ui.form.on('Customer', {
validate(frm) {
if (!frm.doc.mobile_no) {
frappe.throw("Mobile No is required");
}
}
});

Best Practices

  • Use validation on client-side for immediate feedback
  • Use server validation for final checks
  • Prefer frm.set_value() over direct assignment
  • Avoid long frappe.call() chains
  • Keep UI logic separate from business logic
  • Use whitelisted methods with parameters
  • Never expose sensitive logic in JavaScript
Click to rate this post!
[Total: 0 Average: 0]