The DialogV2 class, introduced in v12 alongside AppV2, is a responsive and modern way to present basic choices to users.
Official documentation
Legend
DialogV2.confirm // `.` indicates static method or property
DialogV2#render // `#` indicates instance method or property
The DialogV2 class is a quick and easy way to render a simple application window with a handful of buttons. It is built upon AppV2, so it inherits the dynamic styling for both light and dark mode.
Use DialogV2 if:
It's better to extend ApplicationV2 yourself, e.g. with HandlebarsApplicationMixin
, if:
The DialogV2 class can be accessed via foundry.applications.api.DialogV2
, and the code can be found in yourFoundryInstallPath\resources\app\client-esm\applications\api\dialog.mjs
Any usage of DialogV2 should keep the following in mind.
DialogV2 inherits all the options from ApplicationConfiguration, which has the window
property that includes a field for title
you should always set. You may also want to alter the the classes
array which can be helpful for specifying the styling of your dialogs.
In addition, the options from DialogV2Configuration are all important to implement; buttons
and content
especially are where you usually do the most work to configure the dialog. The only automatic localization for these properties is the label
of any button; use template strings and calls to game.i18n.localize
for defining your content
.
One option in particular that should be used with care is modal
, which causes the dialog to disable the rest of the Foundry UI while it is active. This can be good for simple pauses in a workflow, but any higher complexity dialog should avoid using this property.
API Reference
The callback
property of a DialogV2Button determines the return of that button when using the provided static methods - confirm
, prompt
, and wait
. If no callback is defined or the callback returns a nullish result (null
or undefined
), it will return the value of the mandatory action
property (a string). If the callback returns a value, then that value is the return of the button.
Frequently you may want to grab the value of an input in the dialog - to do so in the callback, button.form.elements
is a Record of all of the input elements with the key being the element's name. So to access an input with name="foo"
you could go button.form.elements.foo.value
. All dialogs wrap the content
provided in a form, that tag does not need to be provided in your html.
This behavior can be modified by passing a function to the submit
property of the options
when constructing the dialog or by overriding the _onSubmit
method in an extension of the class.
There are a few basic ways of invoking DialogV2 that can simplify your code. These are all asynchronous operations. Also keep in mind that any strings should probably be template strings that make calls to game.i18n.localize
or game.i18n.format
as needed; the below examples use static strings for readability, but actual implementations should take advantage of Foundry's localization system.
API Reference
The confirm
static method provides a simple way to get a yes/no response. Yes returns true, no returns false.
const likesIceCream = await foundry.applications.api.DialogV2.confirm({
window: { title: "Sweet Treat Check" },
content: "<p>Do you like ice cream?</p>",
modal: true
})
If needed, the default behavior of the yes
and no
buttons can be overridden by providing properties matching their name in argument object, e.g. yes: { class: "mymodule yes"}
.
API Reference
The prompt
static method gives the user a simple input that can be properly await
ed before proceeding.
const proceed = await foundry.applications.api.DialogV2.prompt({
window: { title: "Proceed" },
content: "<p>Do you wish to continue?</p>",
modal: true
})
If needed, the default behavior of the ok
button can be overridden by providing properties matching their name in argument object, e.g. ok: { class: "mymodule ok"}
.
API Reference
The wait
static method is the most flexible of the three and covers a wide range of uses. While the prior two methods do accept additional buttons, the wait
method requires them.
const method = await foundry.applications.api.DialogV2.wait({
window: { title: "D20 Roll" },
content: "<p>Roll Method?</p>",
modal: true,
// This example does not use i18n strings for the button labels,
// but they are automatically localized.
buttons: [
{
label: "Advantage",
action: "advantage",
},
{
label: "Standard",
action: "standard",
},
{
label: "Disadvantage",
action: "disadvantage",
},
]
})
This sample dialog will return the value advantage
, standard
, or disadvantage
to the method
constant - the values of each button's action
property. If you provide a callback
function, then the return of that function will be used in place of the action
.
The rejectClose
property, by default true
in v12 and false
in v13, causes a dialog to throw an error if it is closed, halting all execution. If you would prefer to continue despite the user closing the dialog, pass rejectClose: false
for the dialog to return null
instead.
Here are some common tips and tricks while working with DialogV2
By default, applications only return based on their buttons inputs. However, a common desire is taking simple inputs, such as situational bonuses for a dice roll or picking an option in a Select. The foundry.applications.fields
namespace provides a number of functions to generate input
and select
elements that can be fed into your content
.
const fields = foundry.applications.fields;
const textInput = fields.createNumberInput({
name: 'foo',
value: 'Starting Value'
});
const textGroup = fields.createFormGroup({
input: textInput,
label: "My text input",
hint: "Optional hint"
});
const selectInput = fields.createSelectInput({
options: [
{
label: "Option 1",
value: 'one'
}, {
label: "Option 2",
value: 'two'
}
],
name: 'fizz'
})
const selectGroup = fields.createFormGroup({
input: selectInput,
label: "My Select Input",
hint: "Another Hint"
})
const content = `${textGroup.outerHTML} ${selectGroup.outerHTML}`
Alternatively, if you are working with data models, the toInput
and toFormGroup
functions can help.
const prop = 'foo.bar';
const field = doc.system.schema.getField(prop);
// If the field doesn't have a `label` and `hint` property,
// or you want to customize the label and hint,
// you can pass them into the first parameter of the function.
const group = field.toFormGroup({}, { value: foundry.utils.getProperty(doc.system, prop) });
const content = group.outerHTML;
However you construct it, that content
can then be passed into a DialogV2 static function.
content
If you have a complex chunk of HTML you want to render that doesn't need re-rendering and can be handled as a dialog, outsourcing the HTML construction to handlebars can be an effective tactic. To do that, use the renderTemplate(path, data)
function which is globally available. The path
argument is a string representing a handlebars file on the server, while data
is an object of properties used to fill in the handlebars. This function is asynchronous to allow fetching the file from the server if it's not in the cache.
const content = await renderTemplate('path/to/template.hbs', data)
const response = await foundry.applications.api.DialogV2.prompt({
window: { title: "Proceed" },
content,
modal: true
})
If all the inputs in your Dialog have a name
property, the FormDataExtended class can help collect that data and return it in a Foundry-friendly way. This is the same class that foundry uses to collect document sheet data.
const data = foundry.applications.api.DialogV2.prompt({
window: { title: "My Dialog" },
content: '<input type="number" name="foobar">',
ok: {
label: "Save",
icon: "fa-solid fa-floppy-disk",
callback: (event, button, dialog) => new FormDataExtended(button.form).object
},
rejectClose: false
})
When working with many inputs, this will give a flat object pairing all of the input name
properties with the values submitted. If you need to transform the object to a nested structure, the foundry.utils.expandObject
function can help.
render
optionAPI Reference
When using confirm
, prompt
, or wait
, you can pass a function to the render
property to trigger when the dialog finishes rendering. This can be useful for adding event listeners to the rendered HTML.
Here are some of the common problems when working with DialogV2
All three of the primary DialogV2 static methods are asynchronous, which means they return a Promise. Handling that promise requires the use of then
or await
. It also means that modules cannot use them in a preCreate
hook, which operates synchronously, but systems can use them in the _preCreate
method on Documents or TypeDataModel, as that function is asynchronous.
DialogV2 does not support re-rendering. If you need to re-render your application, use ApplicationV2 instead.