By Daniel Wood, 13 November 2018
What is a "modal dialog"? When we talk about dialogs we’re actually talking about windows in the FileMaker sense. They could be a card window, or they could be a regular dialog window, or even a floating window or FileMakers own modal window style. Modal refers to a window state where the window cannot be dismissed without the user explicitly making a decision that will lead to the closing of that window.
Rather than wait til the end to check out the demo, we strongly recommend you download and explore the example file as you read. This will help you follow along with the content of the article and help you to understand what we are talking about. The demo file contains a lot of examples and information relevant to the remainder of the article, and you’ll find it easier to follow along the article with the demo file open so that you can explore as you read.
A simple modal dialog just opens and sits there forcing the user to do something. No scripts are running, and it’s up to the user to click a button or do something that has been scripted to carry out an action and close the window. The user is unable to move to any other window in the solution before dealing with the modal dialog. Think of a card window that opens showing the user some preferences. The user can do things on that layout, but has been given a close button to dismiss it. The card window is modal in a sense that the user cannot interact with the underlying parent window. Card windows are modal to the parent window only.
Other modal dialogs such as FileMakers “Dialog” window option, are more modal to the entire solution. When a dialog window is created, the user cannot move to any other window in the solution while the dialog window is open. FileMaker Dialogs are useful but they do suffer from a couple of drawbacks which we don’t like.
Firstly, any script that you execute from within a FileMaker dialog window must reside within the current file. You are unable to run a script in an external file. In some solutions this is a deal breaker. You are also unable to spawn any other temporary windows for the purposes of carrying out actions in a script, you literally are locked into a single window.
It is for these reasons we never use FileMakers dialog window option.
We’ve touched upon a couple of uses of modal dialogs already. One may be used simply to have the user locked into a window, such as editing critical preferences. You would not wish for these to accidentally be left open in a window on-screen. By making it modal, the user must close in order to continue using the solution.
Another typical use to replace FileMakers custom dialog script step in favour of a more appealing dialog built using a layout. A script is run and a modal dialog window is opened showing the user a nice dialog in which they have to make a decision. Based upon the decision they make, a different process is performed.
This last use raises an interesting question. If we are using modal dialogs in scripts, such as replacing a custom dialog, then surely the user decision in that script should be then used in the remainder of the script that initiated it?
To show what we mean, lets look at a diagram:
We have a script that is run, and that script at some point in its execution requires a user input to be made. To do this it opens a new modal dialog. This could be either a card window or a dialog window. The user makes a decision, which the script then continues execution with.
But wait, something is not quite right here? If the script opens a new window waiting for the user to do something, what happens to the execution of the script?
There are 2 possible scenarios here. The first is that upon opening the modal window the script ceases execution, leaving the user on the modal window. The user might then do some input, or click a button. The script that they execute in the window might be responsible for continuing the execution of the original script.
Another scenario is that the initial script upon opening the modal window enters a paused state. The script is still running but stuck until resumed. Upon the user making a decision in the modal window, the script is resumed and can continue execution.
It is this second method that we prefer, and love to develop with. There are a number of reasons why we prefer this method which we’ll cover next.
Reusability of modal dialogs is an absolutely critical concept we need to talk about, and it is where modal dialogs become really powerful. Think of a modal dialog as a function. You call a function, you pass it some information and then the function does some calculations and returns a result.
Modal dialogs can be the same. You initiate a modal dialog. The user does some stuff in the dialog, and the dialog returns a result. Through designing modal dialogs in this fashion, you can build a dialog that can be reused multiple times your solution. The dialog becomes something you invoke whenever required to obtain a certain value.
A classic reusability example
Let’s look at a really simple example of a modal dialog being reused. Let’s say you have a table of contacts in your solution, and in your solution there are multiple different scripts that require you to select a contact. One example might be if you create a job, and in order to dos you must know which contact the job is for. Another might be raising an invoice, a quote, or establishing a relationship between two contacts. All of these processes require a contact to be chosen (or added if they do not exist).
In all of these scripts we write, we could program different dialogs in which the user selects a contact, or we could build a single modal dialog - known as a “Contact Picker” purely for this purpose, and reuse it in all our scripts.
The contact picker is invoked by a script to launch it. The user can then do a search in the picker, or perhaps they can choose to add a new contact. The result of the picker is to simply return the ID of a contact chosen - that’s it. If nothing is returned, we assume the user cancelled and did not select a contact.
Now, if we could invoke this in every other script that requires a contact, we have a nice reusable dialog to suit all situations where a contact is required.
Reusable code is nothing new, nor is reusable dialogs of this fashion. People have been building them many years. However most of the modal dialogs we have seen in other solutions typically involve a certain process mentioned earlier where once the modal dialog is opened, script execution terminates, and is then reestablished upon user decision.
Here, our initial script A is run. Perhaps this is for creating a job lets say. It requires a contact to be chosen, so opens the modal contact picker dialog. Script A then terminates and is no longer running. The user selects a contact in the dialog and upon doing so, it begins execution of another script, B.
There is one big issue here - how does script B know that Script A was initially running and required the contact ID? Moreover, how does script A continue its original execution now that it has terminated?
There are various ways around this. One such way is that B is a decision script, and we store which process was initially running (ie Create Job) in a global variable. Script B checks this, and knows that prior to the dialog being invoked, the “Create Job” process was running, and so returns the contact ID chosen back to this script.
Even in doing so however, any state that was in script A was lost the second it was terminated (unless everything was stored in global variables).
It’s all a bit of a mess, and the more times you use the contact picker dialog in more scripts, the more checks and branches you end up with having to cater for and the more scripts you have to restore.
Fortunately there is a far easier way to structure your modal dialogs.
Never stop your script running!
The simple solution is to never stop your initial script running. Once the modal dialog is invoked, your script enters a paused state, and just sits there… waiting. It is waiting for your modal dialog to finish, and return back to your initial script the contact ID. Once that happens, your script simply carries on and uses the chosen contact ID as required. So easy!
Here’s what it looks like. So much simpler and easier to understand. The other major benefit is that because your script does not terminate all variables and state you have defined in the script is still there just waiting. There is no need to create a script that is responsible for figuring out what was originally running before the modal dialog, because it is still running!
So, how do we do this?
It’s easier than you think. There is a really basic structure to the modal process that we use. First, lets look at what Script A might look like:
This is the typical structure of our initiating script. It calls our modal picker script, and immediately it grabs the result and deals with it. But in reality it’s not immediate. Our picker script can take quite some time to return that result, depending on how long the user farts about in selecting a contact! The key is that the modal script will enter a paused state waiting for the user to do whatever it is they need to do.
Here is the basic structure of our modal script:
We initiate the modal dialog, and then we enter this special loop. Inside the loop the script is paused.
The next line of code looks really out of place - how can we be obtaining a script result when no script was called? I hear lots of heads scratching here. Well, the thing that makes this all possible is the fact that any script the user calls now through clicking a button on the layout can return its result back to this script. So for example if the user clicks on a contact, or clicks a cancel button, whatever that script exits with is passed back here!
There is one thing that all scripts the user runs must do however. They must be set to resume the currently running script - that being our modal script.
Here is what one such button on the layout looks like. We’re running a special script called “Modal Decision”. We are passing the script a parameter that tells it what the user has clicked. Note that the option to Resume current script is used. If you don’t want a button or script run in the modal dialog to continue execution of the modal dialog, simply set it to keep the current script paused.
In this case, we’re passing our decision script a selected contact ID.
Here is our Modal Decision script:
Couldn’t be simpler! We just spit back out exactly what we got in. This tells our modal script what their decision was.
Recall back in our modal script. Once this script resumes it too returns the user decision, which is the contact ID. This ID thus flows back to our original script, where it is picked up and dealt with in script A that continues execution, so simple and elegant!
You’ll note that we have a loop in our modal script. In the example we just showed the loop is irrelevant and not used. But there are times when the user may make a decision in the modal dialog that requires some checks to be carried out before their decision is accepted.
Going back to our contact picker example, what if our dialog has the ability for the user to enter some details for a brand new contact? We might use global fields to allow user input. We may however have some requirements that a contact name is specified, or that an email address entered is in a valid format. These are checks that must be done before user decision is accepted.
It is in this loop that we can carry out those checks. When the user decision is received, we can carry out checks, and only if all checks are successful do we exit the script with the result. If checks fail, the loop simply repeats, putting the user back into the paused state, thus waiting for them to remedy their errors.
Here is one such example. The user has chosen to add a new contact, and we can assume they have attempted to fill out some global fields for the new contacts details. Upon resuming, this script does a check - have they entered a name for the new contact? If not, a custom dialog is shown to the user saying they need to enter a name, and they are taken back to the name field.
If that check does not happen, the alternative is to create the contact, and return the new contacts ID as the result. The new contact ID goes back to the original script.
Here is the basic flow of process for our modal structure:
1. Script A begins execution
2. Executes Modal Script
3. Modal script initialises dialog and pauses in a loop
4. User interacts with dialog, eventually making a decision
5. Decision is returned back to modal script to resume execution
6. Modal Script carries out checks, processes decision, and returns result if all checks are valid
7. Result returned back to Script A
8. Script A continues processing using the information returned from modal dialog.
We hope that this is pretty straightforward and easy enough to follow. The 3 main keys to this process are:
1. Entering a paused state in the modal script
2. Being able to detect what action user has done in modal script, by way of retrieving a script result
3. Being able to resume execution and return result back to originating script
With this method, one thing you may need to pay attention to is how a user cancels the modal dialog. A button is the easiest way, and have that button return a decision of “Cancel” back to your modal script to handle as required.
Because our modal dialog is paused, the standard “X” in the top left corner of the window to close will not work, nor will the standard close menu item. To work around this, you can add a custom menu to the modal layout. If you override the close menu item with your own decision script (the exact same one you would attach to your cancel button in the dialog) then you can work around this and close the dialog by using the menu item, or shortcut.
However because modal windows are designed to be modal, we would recommend the user have to explicitly press a cancel button if they wish to cancel the process.
We have already covered the main reasons why this method of implementing a modal dialog is great - simple, reusable, and no need to terminate your parent script However one thing that is also great is something we touched upon right at the start of the article. Recall that FileMaker “Dialog” modal window is unable to execute scripts in other files while open? Well with this method you can do that, as well as spawn new temporary windows for use in your scripts, all while keeping the window truly modal.
As with all of our articles we produce we like to provide a detailed example file to go along with it. It’s not enough to just read how something is done, you should be able to see it in action and explore how it works yourself. Please find attached the example file below.