1. Ad hoc dialogs with open_dialog

Ad hoc dialogs let an ACF function or a FileMaker calculation collect parameters without building a dedicated FileMaker layout. The dialog is described by JSON, opened as a modal native window, and returns the selected button and field values as JSON.

  1. Ad hoc dialogs with open_dialog
    1. Use the Dialog Configurator first
      1. Try it out
    2. Opening a dialog in ACF
    3. Opening a dialog from FileMaker
    4. Default values
    5. Applying values to a dialog definition
    6. Validation loop example
    7. Dialog actions
    8. Action parameters
    9. Action return modes
    10. Field buttons
    11. Change events
    12. Dynamic option lists
    13. Horizontal radio grids
    14. Read-only fields and validation
    15. Prefilling from SQL JSON INTO
    16. Hidden fields
    17. Variations
    18. Common field types
    19. Practical pattern

The most important functions are:

JSON result = open_dialog(JSON dialogDefinition);
JSON newDefinition = dialog_apply_values(JSON dialogDefinition, JSON values);

From FileMaker calculations, the same features are available as external plugin functions using serialized JSON text:

ACF_OpenDialog(dialogJSON)
ACF_DialogApplyValues(dialogJSON ; valuesJSON)

1.1. Use the Dialog Configurator first

This page describes the current dialog functionality in ACF Plugin 1.7.9.9, available for both macOS and Windows from the download area.

The recommended way to build dialogs is the browser-based Dialog Configurator 1.8.0:

Dialog-Config-180

https://horneks.no/manuals/ACF/tools/dialog-configurator-1.8.0.html

The configurator is a visual editor for dialog JSON:

The generated FileMaker calculation is especially useful when you want to use ACF_OpenDialog directly from a FileMaker script without writing ACF code. It builds the dialog definition with FileMaker's native JSONSetElement function and wraps it in ACF_OpenDialog(...), so the result can be pasted into a single Set Variable calculation.

The ACF source output generates a ready-to-use JSON definition = ...; JSON result = open_dialog(definition); pattern using JSON() and JSONarray().

This is usually much faster and less error-prone than writing the full JSON object manually. The JSON specification is still useful when you want to build or alter dialog definitions dynamically.

Dialog-config-output

1.1.1. Try it out

You can test a generated dialog directly from FileMaker without writing an ACF function.

  1. Download ACF Plugin 1.7.9.9 for your platform if you have not already installed it. If you have an older version, update to the latest version.
  2. Install the plugin.
  3. Restart FileMaker after installing or updating the plugin.
  4. Open the Dialog Configurator 1.8.0: https://horneks.no/manuals/ACF/tools/dialog-configurator-1.8.0.html
  5. Add a few fields of different types. Try text fields, date ranges, select fields, radio buttons, columns, or hidden values.
  6. Press Download JSON when you are finished. This gives you a file you can load back into the configurator later if you want to make changes.
  7. Scroll down to the output area at the bottom of the configurator.
  8. In the right output pane, press FileMaker.
  9. Copy the generated FileMaker calculation.
  10. Open FileMaker.
  11. Choose Tools > Data Viewer.
  12. Go to the Watch tab and press the small + button in the bottom right.
  13. Make sure automatic evaluation is unchecked.
  14. Paste the generated FileMaker calculation.
  15. Press Evaluate.

The generated calculation calls ACF_OpenDialog(...), so your new dialog should open immediately. When you press OK, the returned JSON result appears in the Data Viewer result pane.

1.2. Opening a dialog in ACF

The open_dialog function accepts one JSON parameter and returns a JSON result. It blocks the ACF function until the user clicks a button or closes the dialog.

function AskForCustomer ()

    JSON definition = JSON(
        "title", "Customer search",
        "subtitle", "Enter search criteria.",
        "size", JSONarray(560, 360),
        "labelPosition", "left",
        "fields", JSONarray(
            JSON("name", "Customer", "label", "Customer", "type", "text", "width", "full"),
            JSON("name", "Status", "label", "Status", "type", "select",
                "options", JSONarray("Open", "Closed", "All")),
            JSON("name", "FromDate", "label", "From date", "type", "date")
        ),
        "values", JSONarray("", "Open", ""),
        "buttons", JSONarray(
            JSON("id", "ok", "label", "Search", "default", true),
            JSON("id", "cancel", "label", "Cancel", "cancel", true)
        )
    );

    JSON result = open_dialog(definition);
    return result;
end

This gives this dialog:

AskForCustomer

A typical result:

{
  "button": "ok",
  "buttonLabel": "Search",
  "values": {
    "Customer": "Horneks",
    "Status": "Open",
    "FromDate": "2026-06-20"
  }
}

1.3. Opening a dialog from FileMaker

Use ACF_OpenDialog(dialogJSON) when you want to open a dialog directly from FileMaker scripts or calculations.

The Dialog Configurator can generate a FileMaker calculation that builds the JSON definition and calls ACF_OpenDialog in one expression. This is the recommended copy-and-paste workflow for FileMaker-only use.

For FileMaker calculation, press the "FileMaker" button over the tab:

Dialog-config-outputFM

Paste this into the calculation and you are all set.

Set Variable [ $result ; Value: ACF_OpenDialog ( Let ( [
  ~json = JSONSetElement ( "{}" ;
    [ "title" ; "Customer search" ; JSONString ] ;
    [ "subtitle" ; "Enter search criteria." ; JSONString ] ;
    [ "size[0]" ; 560 ; JSONNumber ] ;
    [ "size[1]" ; 360 ; JSONNumber ] ;
    [ "labelPosition" ; "left" ; JSONString ] ;
    [ "rows[0].fields[0].name" ; "Customer" ; JSONString ] ;
    [ "rows[0].fields[0].label" ; "Customer" ; JSONString ] ;
    [ "rows[0].fields[0].type" ; "text" ; JSONString ] ;
    [ "buttons[0].id" ; "ok" ; JSONString ] ;
    [ "buttons[0].label" ; "Search" ; JSONString ] ;
    [ "buttons[0].default" ; True ; JSONBoolean ]
  )
] ;
  ~json
) ) ]
Set Variable [ $button ; Value: JSONGetElement ( $result ; "button" ) ]
Set Variable [ $customer ; Value: JSONGetElement ( $result ; "values.Customer" ) ]

ACF_OpenDialog returns serialized JSON text. In ACF code, open_dialog returns a native ACF JSON value.

This avoids using FileMaker layouts or global fields just to collect temporary script parameters. The dialog values exist only in the returned JSON result.

1.4. Default values

Default field values are stored in values arrays beside the matching fields arrays. The first value belongs to the first field, the second value belongs to the second field, and so on.

{
  "fields": [
    { "name": "Customer", "label": "Customer", "type": "text" },
    { "name": "Status", "label": "Status", "type": "select",
      "options": ["Open", "Closed", "All"] }
  ],
  "values": ["Horneks", "Open"]
}

For rows with columns, each column has its own fields and values arrays:

{
  "columns": [
    {
      "fields": [
        { "name": "FromDate", "label": "From", "type": "date" }
      ],
      "values": ["2026-06-29"]
    }
  ]
}

Range fields use a two-element value array:

{
  "fields": [
    { "name": "Period", "label": "Period", "type": "dateRange", "separator": "-" }
  ],
  "values": [["2026-06-01", "2026-06-29"]]
}

The configurator writes these value arrays automatically. The JSON, ACF source, and FileMaker calculation outputs all use the same dialog definition object.

1.5. Applying values to a dialog definition

dialog_apply_values(definition, values) takes a dialog definition and a flat JSON object with field-name/value pairs. It returns a new dialog definition with the values placed in the correct rows, columns, variants, and field definitions.

This means the code that pre-fills a dialog does not need to know where each field is located in the layout.

JSON values = JSON(
    "Customer", "Horneks",
    "Status", "Open",
    "FromDate", "2026-06-20"
);

JSON filledDefinition = dialog_apply_values(definition, values);
JSON result = open_dialog(filledDefinition);

The values parameter can also be a dialog result object. If it contains a top-level values object, that object is used automatically:

JSON result = open_dialog(definition);
JSON retryDefinition = dialog_apply_values(definition, result);

From FileMaker:

Set Variable [ $filledDefinition ; Value: ACF_DialogApplyValues ( $definition ; $valuesJSON ) ]
Set Variable [ $result ; Value: ACF_OpenDialog ( $filledDefinition ) ]

1.6. Validation loop example

Named staticText fields can be updated by dialog_apply_values. This is useful for validation messages.

function PasswordDialog ()

    JSON definition = JSON(
        "title", "Login",
        "size", JSONarray(520, 300),
        "labelPosition", "left",
        "fields", JSONarray(
            JSON("name", "err", "type", "staticText", "text", "",
                "decoration", JSON("size", 14, "color", "#ff2600",
                    "align", "center", "weight", "bold")),
            JSON("name", "UserName", "label", "User name", "type", "text"),
            JSON("name", "Password", "label", "Password", "type", "password")
        ),
        "buttons", JSONarray(
            JSON("id", "ok", "label", "OK", "default", true),
            JSON("id", "cancel", "label", "Cancel", "cancel", true)
        )
    );

    JSON result = open_dialog(definition);

    if (result["button"] == "ok" && length(string(result["values.Password"])) < 8) then
        result["values.err"] = "Password is too short";
        JSON retryDefinition = dialog_apply_values(definition, result["values"]);
        result = open_dialog(retryDefinition);
    end if

    return result;
end

Empty staticText fields do not occupy vertical space. If a fixed blank area is desired, use a single space as the initial text.

1.7. Dialog actions

Dialogs can call ACF functions while the dialog is still open. Actions can be attached to footer buttons, field-level buttons, and field change events.

Action callbacks are useful for:

An action object has this basic shape:

{
  "function": "FunctionName",
  "params": ["{FieldName}", ":localVariable", "{}"],
  "returnAction": "set",
  "target": "TargetField"
}

The function is resolved when the dialog opens. If the callback cannot be found or the parameter structure is invalid, the dialog reports the problem immediately instead of waiting until the user clicks a button.

1.8. Action parameters

Action parameters are written as strings in the JSON definition. The dialog resolves them before calling the ACF function.

Syntax Meaning
{FieldName} Current value of a field in the dialog.
{} Current dialog result JSON, in the same shape as the final open_dialog return value. This is useful for functions that inspect or modify several values.
:localName Local variable in the calling ACF function.
$var, $$var, Table::Field FileMaker expression-style value, evaluated through the plugin.
Any other text Literal string parameter.

Parameters are passed in the order they appear in the params array, matching the called ACF function signature.

When writing raw JSON, use the string token "{}". When building the definition with ACF's JSON() and JSONarray() helpers, JSONarray("{}") is also accepted even if the builder stores it as an empty JSON object internally.

1.9. Action return modes

The returnAction value controls what happens with the ACF function result.

returnAction Description
apply Treat the function result as JSON values and apply them to the current dialog. This is the default.
set Put the function result into the field named by target.
setOptions Replace the option list for the field named by target. Works with select, list, and radio.
reload Replace the current dialog definition with the JSON definition returned by the callback, keeping the dialog open.
close Let the button continue closing the dialog after the action has run.

For setOptions, the callback can return:

Blank options are preserved. This is useful when the first option should mean "no selection".

1.10. Field buttons

Use a field with "type":"button" when the button belongs near the data it affects.

{
  "name": "ChoosePath",
  "type": "button",
  "buttonLabel": "Choose...",
  "action": {
    "function": "SaveFileDialog",
    "params": ["Save report", ":folder", ":target"],
    "returnAction": "set",
    "target": "Path"
  }
}

This pattern keeps utility buttons close to their target fields instead of placing everything in the footer.

1.11. Change events

Regular fields can use onChange. Range fields can additionally use onChangeFrom and onChangeTo, so the two sides of a range can trigger different logic.

{
  "name": "Name",
  "label": "Name",
  "type": "text",
  "onChange": {
    "function": "testFormatName",
    "params": ["{Name}"],
    "returnAction": "set",
    "target": "Name"
  }
}

For a date range:

{
  "name": "Period",
  "label": "Period",
  "type": "dateRange",
  "separator": "-",
  "onChangeFrom": {
    "function": "testDateRange30",
    "params": ["{Period}"],
    "returnAction": "set",
    "target": "Period"
  }
}

In this example the callback can return a two-value JSON array, such as ["2026-06-01", "2026-07-01"], and the range field is updated in one operation.

1.12. Dynamic option lists

Actions can reload the options for select, list, and radio controls with returnAction:"setOptions".

function myValueListItems ( string valuelist )

    string expr = format ('ValueListItems(""; "%s")', valuelist);
    string items = eval(expr);
    return explode("\r", items);
end
{
  "name": "ReloadReports",
  "type": "button",
  "buttonLabel": "Reload",
  "action": {
    "function": "myValueListItems",
    "params": ["ReportNames"],
    "returnAction": "setOptions",
    "target": "Report"
  }
}

This makes it possible to filter a report list, a destination list, or similar choices without closing and reopening the dialog.

1.13. Horizontal radio grids

Radio fields can be displayed vertically or as a horizontal grid.

{
  "name": "Destination",
  "label": "Destination",
  "type": "radio",
  "layout": "horizontal",
  "columns": 3,
  "options": ["Paper", "Excel", "PDF"]
}

If columns is omitted, the dialog uses an automatic column count. On macOS, reloaded option lists are measured again so longer option labels can reduce the column count instead of being truncated.

1.14. Read-only fields and validation

Fields can be made read-only with readonly:true or readOnly:true. For range fields, readonlyFrom / readOnlyFrom and readonlyTo / readOnlyTo can lock only one side of the range.

Date and numeric-style workflows can use a validation object with min and max values. For ranges, validation can be split into from and to sections.

{
  "name": "DuePeriod",
  "label": "Due period",
  "type": "dateRange",
  "readonlyFrom": true,
  "validation": {
    "to": {
      "min": "2026-06-21",
      "max": "2026-07-21"
    }
  }
}

Validation is intentionally local to the dialog control. For database lookups, cross-field rules, or company-specific policy checks, use an action callback and apply the returned values or error message.

1.15. Prefilling from SQL JSON INTO

ExecuteSQL and ExecuteMySQL can write a result set directly into a JSON variable when the SQL ends with INTO :jsonVariable.

This pairs nicely with dialog_apply_values.

function EditPackageSettings ()

    JSON definition = JSON(
        "title", "Package settings",
        "labelPosition", "left",
        "fields", JSONarray(
            JSON("name", "PackageKey", "label", "Package key", "type", "text"),
            JSON("name", "Name", "label", "Name", "type", "text", "width", "full")
        ),
        "buttons", JSONarray(
            JSON("id", "ok", "label", "OK", "default", true),
            JSON("id", "cancel", "label", "Cancel", "cancel", true)
        )
    );

    JSON data;
    string sql = "SELECT PrimaryKey AS PackageKey, Package_Name AS Name
    FROM ACF_Packages
    INTO :data";

    string res = ExecuteSQL(sql);

    if (data["success"] && data["rows_found"] > 0) then
        JSON firstRow = data["result[1]"];
        definition = dialog_apply_values(definition, firstRow);
    end if

    return open_dialog(definition);
end

1.16. Hidden fields

Dialog definitions can include hidden values that are returned with the visible field values. Hidden fields are useful for primary keys, mode flags, record state, or other data that should travel with the dialog result without being displayed.

{
  "title": "Edit customer",
  "hidden": {
    "CustomerID": "A123",
    "mode": "edit"
  },
  "fields": [
    { "name": "CustomerName", "label": "Customer", "type": "text" }
  ]
}

Result:

{
  "button": "ok",
  "values": {
    "CustomerID": "A123",
    "mode": "edit",
    "CustomerName": "Horneks"
  }
}

1.17. Variations

A dialog can contain alternative field sets controlled by a selector. This is useful when one dialog supports several report types, import modes, or settings pages.

Important root options:

Key Description
selector Defines the controlling selector and its options.
variants Field layouts keyed by selector option values.
variationStyle "select" uses a popup selector. "tabs" uses tab-style variant selection.
variationResult "current" returns only the selected variant's fields. "all" returns values from all variants.

Example:

{
  "title": "Report settings",
  "variationStyle": "tabs",
  "variationResult": "current",
  "selector": {
    "name": "ReportSource",
    "label": "Report source",
    "options": ["Offers", "Orders", "Invoices"],
    "value": "Orders"
  },
  "variants": {
    "Orders": {
      "fields": [
        { "name": "OrderNo", "label": "Order no.", "type": "text" }
      ]
    },
    "Invoices": {
      "fields": [
        { "name": "InvoiceNo", "label": "Invoice no.", "type": "text" }
      ]
    }
  }
}

With variationResult set to "all", fields with the same name in multiple variants are returned as arrays, following the same duplicate-name rule used elsewhere in dialog results.

1.18. Common field types

Type Description
text Single-line text field.
password Single-line masked text field. The result contains the clear typed value.
textarea Multi-line text area.
checkbox Boolean checkbox.
select Popup selector.
radio Radio-button group.
list Selectable list area.
date Date field with popup calendar.
time Time field.
dateRange Two date fields with a separator.
textRange Two text fields with a separator.
staticText Text block, often used for descriptions or validation messages.
button Field-level button that can run an action callback.

Most dialogs should be built with the configurator, but these field names are useful when reading or modifying generated JSON.

1.19. Practical pattern

For many workflows, the cleanest pattern is:

  1. Build the dialog definition in the configurator.
  2. Store the JSON definition in FileMaker or in an ACF function.
  3. Fetch current values as a flat JSON object.
  4. Use dialog_apply_values to prefill the dialog.
  5. Use open_dialog or ACF_OpenDialog to collect edits.
  6. Validate the result and reopen with a named staticText message if needed.

This keeps FileMaker layouts free of global utility fields and keeps dialog logic close to the code that uses the result.