Frappe gives you a few ways to show modals and messages from client code. For a quick message or a yes/no question, use frappe.msgprint and frappe.confirm. For a one-off form, use frappe.prompt. For a custom modal with several fields and your own buttons, build a frappe.ui.Dialog.
frappe.confirm(__("Delete this task?"), () =>
frappe.db.delete_doc("Task", "TASK-0001"),
);
frappe.msgprint
frappe.msgprint(message, title) shows a message in a modal. Pass a string, or an object for more control.
frappe.msgprint(__("Saved successfully"));
frappe.msgprint({
title: __("Heads up"),
message: __("This task is overdue."),
indicator: "red",
});
For a brief non-blocking toast instead of a modal, use frappe.show_alert(message, seconds):
frappe.show_alert({ message: __("Copied"), indicator: "green" }, 5);
frappe.throw(message) shows an error message and stops execution by throwing. Use it in validation:
if (!frm.doc.subject) {
frappe.throw(__("Subject is required"));
}
frappe.confirm
frappe.confirm(message, on_yes, on_no) asks a yes/no question and runs the matching callback.
frappe.confirm(
__("Submit this invoice?"),
() => frm.save("Submit"),
() => console.log("cancelled"),
);
frappe.warn(title, message_html, on_proceed, primary_label, minimizable) is similar but styled as a warning, with a red primary button. The message can be HTML. Set minimizable to let the user shrink the dialog instead of answering right away.
frappe.warn(
__("Are you sure you want to proceed?"),
__("There are unsaved changes on this page."),
() => frm.reload_doc(),
__("Proceed"),
true, // minimizable
);
frappe.prompt
frappe.prompt(fields, callback, title, primary_label) opens a dialog of input fields and gives you the entered values. Pass a single field, an array of fields, or a string for one Data field.
frappe.prompt(
[
{
fieldname: "reason",
fieldtype: "Small Text",
label: __("Reason"),
reqd: 1,
},
{ fieldname: "notify", fieldtype: "Check", label: __("Notify owner") },
],
(values) => {
console.log(values.reason, values.notify);
},
__("Close Task"),
__("Submit"),
);
Each field uses the same docfield shape as a DocType field: fieldtype, fieldname, label, options, reqd, and default.
frappe.ui.Dialog
For full control, build a frappe.ui.Dialog. It holds its own set of fields (it extends frappe.ui.FieldGroup) and lets you define the primary and secondary buttons.
let d = new frappe.ui.Dialog({
title: __("Assign Task"),
fields: [
{
label: __("Assign To"),
fieldname: "user",
fieldtype: "Link",
options: "User",
reqd: 1,
},
{
label: __("Comment"),
fieldname: "comment",
fieldtype: "Small Text",
},
],
primary_action_label: __("Assign"),
primary_action(values) {
console.log(values.user, values.comment);
d.hide();
},
});
d.show();
The primary_action callback receives the dialog values. Useful methods on the dialog:
show()andhide(): open and close it.get_values(): read all field values as an object (returns nothing if a required field is empty).get_value(fieldname)andset_value(fieldname, value): read or write one field.set_df_property(fieldname, property, value): change a field property at runtime, likeoptionsorhidden.get_field(fieldname): get the control object for a field. See Controls.set_primary_action(label, fn)andset_secondary_action(fn): set the buttons after construction.
// react to a field change inside the dialog
let user_field = d.get_field("user");
user_field.df.onchange = () => {
let user = d.get_value("user");
d.set_df_property("comment", "hidden", !user);
};
The size option accepts "small", "large", or "extra-large". Set static: true to stop the dialog from closing when the backdrop is clicked.
frappe.new_doc
frappe.new_doc(doctype, route_options, init_callback) opens a new document. If Quick Entry is enabled for the DocType, a Quick Entry dialog opens with the important fields. Otherwise it routes to the full form.
frappe.new_doc("Task");
route_options is an object of field values to pre-fill. It sets any field on the new document, except fields marked no_copy.
frappe.new_doc("Task", { subject: "New Task" });
init_callback runs once just before the user can edit the new document. For a Quick Entry form it receives the dialog object; for the full form it receives the new doc. Use it to set fields that route_options cannot, such as child table rows.
frappe.new_doc("Task", { subject: "New Task" }, (doc) => {
doc.description = "Do what's necessary";
});
frappe.new_doc returns a Promise that resolves once the form or dialog is ready.
frappe.ui.form.MultiSelectDialog
A MultiSelectDialog shows filter fields above a checkbox list of documents. The user filters, selects rows, and your action runs with the selected names. It is handy for pulling records from one DocType into another.
new frappe.ui.form.MultiSelectDialog({
doctype: "Material Request",
target: cur_frm,
setters: {
schedule_date: null,
status: "Pending",
},
add_filters_group: 1,
get_query() {
return {
filters: { docstatus: ["!=", 2] },
};
},
action(selections) {
console.log(selections); // array of selected document names
},
});
Options:
doctype: the source DocType to list. Use"[Select]"to list passed-in values instead of a DocType.target: the form or object the dialog acts on.setters: an object (or array of docfields) that becomes filter fields. The keys also show up as columns in the list. Values seed the initial filter.add_filters_group: set to1to add the same filter builder used in list view, below the setters.get_query: a function returning{ query, filters }.filtersnarrows the list.queryis a dotted path to a server method that returns the rows.primary_action_label: label for the primary button. Defaults to "Get Items".action(selections, args): runs on the primary button with the array of selected names.
To use a custom server method, pass it as query in get_query:
new frappe.ui.form.MultiSelectDialog({
doctype: "Material Request",
target: cur_frm,
setters: { status: null },
get_query() {
return {
query: "dotted.path.to.method",
filters: { docstatus: ["!=", 2], supplier: "John Doe" },
};
},
action(selections) {
console.log(selections);
},
});
The secondary button is labelled "Make {DocType}" and routes to a new document of that DocType.
Selecting child rows
Set allow_child_item_selection: 1 with a child_fieldname to let the user pick individual child rows instead of whole parent documents. child_columns lists the child fields to show.
new frappe.ui.form.MultiSelectDialog({
doctype: "Material Request",
target: cur_frm,
setters: { status: null },
add_filters_group: 1,
allow_child_item_selection: 1,
child_fieldname: "items", // child table fieldname
child_columns: ["item_code", "qty"], // child fields to show
get_query() {
return {
filters: { docstatus: ["!=", 2] },
};
},
action(selections, args) {
console.log(args.filtered_children); // selected child row names
},
});
The dialog shows a "Select Individual Items" checkbox. When toggled on, it lists the child rows from the queried parents so the user can filter and pick them. The selected child names come back in args.filtered_children in the action callback.