Framework Git

Framework Git

Open in ChatGPT
Ask ChatGPT about this page
Open in Claude
Ask Claude about this page

Dialog API

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() and hide(): open and close it.
  • get_values(): read all field values as an object (returns nothing if a required field is empty).
  • get_value(fieldname) and set_value(fieldname, value): read or write one field.
  • set_df_property(fieldname, property, value): change a field property at runtime, like options or hidden.
  • get_field(fieldname): get the control object for a field. See Controls.
  • set_primary_action(label, fn) and set_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 to 1 to add the same filter builder used in list view, below the setters.
  • get_query: a function returning { query, filters }. filters narrows the list. query is 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.

Last updated 2 hours ago
Was this helpful?
Thanks!