Dynamic Forms with JavaScript and FileMaker Web Viewers

Using the FileMaker platform we can harness the power and wow-factor of modern web interfaces. This post shows how we can build dynamic data-entry forms for use in our apps.

Dynamic Forms

When we say a form is “dynamic”, we mean that the form’s appearance can be altered at multiple points before and during the data entry process. Traditional FileMaker layouts are semi-dynamic to a degree, but they lack the auto-sliding of web interfaces and have very limited animation options.

As well as giving us access to new widgets and controls, we will see how web interfaces allow us to be specific about which controls show on layouts without worrying about running out of space, dealing with gaps between fields or a general excess of white space.

Stacked It: an app about Things

Our dynamic web form is demonstrated in Stacked It, an app about Things. In Stacked It these Things can be physical, remembered or imaginary β€” anything! You add Things to a Stack, starting out as shapes, and then scroll through it at your leisure, making tweaks along the way.

Download the app demo file, Stacked_It.fmp12 [453KB]. (FileMaker 19 or later is required.)

Stacked It is a simple app but it has some requirements that have been traditionally difficult to deliver in FileMaker. Below we list these requirements and demonstrate their delivery.

The interface for our Stack should be visual and dynamic

The Stacked It demo
Add a new Thing, and now you’ve Stacked It (Click image to open in new tab)

Users can change the shape of a Thing by dragging its edges

Shape Demo
Resize a shape by grabbing and dragging (Click image to open in new tab)

Users can dynamically add and edit structured data about Things

Custom Field Demo
Dynamically add a new field, including a label (Click image to open in new tab)

We will now see how these these dynamic form (and interface) requirements were fulfilled in FileMaker with the help of web technologies.

Web viewer tools in FileMaker

We leveraged two key tools to fulfill our requirements:

  1. Perform JavaScript in Web Viewer β€” a FileMaker script step
  2. the FileMaker.PerformScript β€” a JavaScript function

From scripts to web viewers: Perform JavaScript in Web Viewer script step

The Perform JavaScript in Web Viewer script step allows us to manipulate structure, style, and content in a FileMaker web viewer. As the name suggests, the method for doing this is JavaScript. Specifically, we can call a JavaScript function in a web viewer and include (text) parameters in that function call.

Perform JavaScript in Web Viewer script step

From web viewers to scripts: FileMaker.PerformScript JavaScript function

FileMaker.PerformScript is a special JavaScript function accessible within web viewers. It allows us to run a FileMaker Script directly from a web viewer. To use this function the new option “Allow JavaScript to perform FileMaker scripts” must be turned on in the web viewer setup.

Web Viewer setup example

FileMaker.PerformScript('Handshake', 'Hello FileMaker');

More information on these features can be found in the FileMaker help.

Limitations πŸ™

While immensely powerful, both of these features share the same limitation: They cannot directly get results from the scripts or functions they run. That is, the result of the function called by the Perform JavaScript in Web Viewer step is not stored. And the result of the FileMaker.PerformScript function in JavaScript is “undefined” even if that script returns a result in its “Exit Script” step. Read on to see how a well-implemented web viewer workflow can overcome these limitations.

A web viewer workflow without limitations πŸ™‚

To allow us to utilize script and function results in our web integrations, we follow a workflow that manages the movement of data (and metadata) between our web components and our FileMaker solution. The workflow contains four stages:

  1. Web project is deployed in a web viewer
  2. Web project sends a request to FileMaker
  3. FileMaker responds to Web project
    Note: Steps 2 and 3 may repeat multiple times before user interaction
  4. User interacts with the web viewer, triggering additional flows of requests and responses to and from FileMaker.

Workflow principles

The implementation of this workflow is opinionated. This means that we are using the Perform JavaScript in Web Viewer script step and FileMaker.PerformScript function in very specific ways. The principles of the workflow are:

  • A single FileMaker controller script should receive and route all script requests from web viewers.
    • In Stacked It this is the “Web Viewer Receiver” script
  • A single FileMaker wrapper script should handle the Perform JavaScript in Web Viewer script step.
    • In Stacked It this is the “Run Function In Web Viewer” script.
  • To handle the asynchronous nature of FileMaker scripts called from the web viewer a Deferred object should be used.
    • In the Stacked It web projects, the Deferred function is defined in deferred.js and used in fm-relay.js.
    • Deferred objects allow promises to be fulfilled by code outside the promise scope, in our case by the Perform JavaScript in Web Viewer script step. More information on Deferred objects can be found here.

Dynamic Form Creator

The dynamic form in Stacked It shows our workflow in action. It is built using a dynamic form creator module, which consists of FileMaker scripts (found in the Dynamic Form Creator folder) and a web project. The web project is stored within FileMaker. You can access the web projects via the settings icon on the main Stack layout. On the settings page you can also access a table listing all web viewers in the app. Each web viewer is linked to a web project.

Stacked It uses the form creator for data entry. Let’s follow our workflow through for the two data entry processes in the app: “Add Thing” and “Edit Thing”

Add Thing

Step 1: Web project is deployed in web viewer

We take the first step in our workflow when we press the “Add Thing” button. This runs the “Add Thing In Web Viewer” script, which opens a card window and creates a new record (without committing it). Then the appropriate project is loaded into the web viewer on the layout. The name of the web viewer is passed to the web code in a query parameter, which allows FileMaker scripts performed by the web viewer to be traced back to their source.

Step 2: Web project makes a request to FileMaker

Once the web viewer is loaded, its JavaScript code requests data from FileMaker so it can build the form.

Step 3: FileMaker sends a response to web project

FileMaker sends the form structure and data back to the web project, which uses it to create and populate the form inputs and controls and render the form in the web viewer.

Steps 2 and 3 Repeat

We are not ready to allow the user to enter data in the form yet. First we want to ensure our card window is large enough to fit the generated form. So, once our form is finished rendering, we request that FileMaker resizes the card window to height of our form content. We achieve this ordering of events using a “promise chain” which is a common JavaScript technique for performing asynchronous actions in sequence, with each action building on the result of the last.

The code below shows our promise chain for all iterations of steps 2 and 3.

/* Once the DOM content is loaded... */
document.addEventListener('DOMContentLoaded', function(e){

  /* And the FileMaker.PerformScript function is ready... */
  FmUtilities.whenFmReadyRunFunction(function(){

    /* Store the values passed in the query parameters */
    const webViewerName = FmUtilities.getUrlParameter("webViewerName");
    const dataEntryProcessId = FmUtilities.getUrlParameter("dataEntryProcessId");
    
    /* Create objects to interact with FileMaker and build the form */
    window.fmRelay = new FmRelay(webViewerName);
    window.formBuilder = new FormBuilder(dataEntryProcessId);

    /* Request form structure and data from FileMaker... */
    const formBuildPromise = window.fmRelay.runFmProcessAsync({
      PROCESS_NAME: 'Build Thing Form'
    });

    /* Then create and render the form and return the content height... */
    formBuildPromise.then(function(result) {
      window.formBuilder.createForm(JSON.parse(result));
      window.formBuilder.deployForm();
      return document.body.clientHeight;
    })

    /* Then resize the FileMaker window based on the content height */
    .then(function(result) {
      return window.formBuilder.resizeWindow('', result + 50);
    });
  })
  
});

Step 4: User interacts with web viewer, triggering additional flows of data to and from FileMaker

Now the user is able to interact with our form. They can enter data and add additional fields. Every time a change is made to form data a FileMaker script is triggered. This updates the FileMaker field to the value in the corresponding form field.

When the user has finished entering data they can save their changes by clicking the “Stack It” button. This button triggers a promise chain. The first action in the chain is a FileMaker script that commits the FileMaker record. If this is successful, another FileMaker script runs that closes the web viewer card window, thus concluding the data entry session.

The promise chain and supporting function is shown below:

/* When the user clicks the 'Stack It' button */
this.saveFmRecordAndGoBackToStack = function() {
  
  /* Attempt to commit the FileMaker record */
  const promise = this.saveFmRecord();

  /* Then complete the data entry session */
  promise.then(function(result){
    console.log('Saved');
    FmRelay.runFmProcess({
      PROCESS_NAME: 'Complete Thing Data Entry',
      NEW_RECORD: this.newRecord,
      ID_THING: document.getElementById('THING::ID').value,
      DATA_ENTRY_PROCESS_ID: this.dataEntryProcessId
    });
  }.bind(this))

  /* If there is an error in the promise chain log it to the console*/
  .catch(function(error){
    console.log('Record not saved: ' + error);
  });
}

/* Function to commit the record in FileMaker */
this.saveFmRecord = function() {
  const parameter = {
    PROCESS_NAME: 'Commit Record',
  };
  return window.fmRelay.runFmProcessAsync(parameter);
}

The alternative path is that the user wants to discard their changes and clicks the “Cancel” button. In this case, rather than running a promise chain, a single FileMaker script runs that reverts the record and closes the web viewer card window.

Edit Thing

The edit process is similar to the add process but it has a key difference: It is started from within our Stack web viewer when the user clicks on a Thing. This means that we are already in a web viewer workflow when the edit process starts. And when we complete our edit, we return to that initial web viewer workflow. It turns out that a workflow can itself contain workflows and, crucially, information can be passed between these workflows.

When the user clicks on a Thing in the Stack to start a new web viewer workflow, our existing workflow initiates that workflow as part of a promise chain:

const editPromise = window.fmRelay.runFmProcessAsync({
  PROCESS_NAME: 'Edit Thing',
  ID_THING: e.target.getAttribute('data-thing-id'),
  DO_NOT_COMPLETE_PROCESS: true
},0);
editPromise.then(function(result){
  this.updateThing(result);
  return;
}

When constructing the FileMaker script call that starts our edit workflow, we set the DO_NOT_COMPLETE_PROCESS flag to true. We also include a timeout parameter of 0. These two settings ensure that the promise will remain unfulfilled until the edit workflow is completed. To fulfill the promise, our new process will require the process ID of the Edit process. Our FileMaker script “Edit Thing In Web Viewer” takes care of this by passing the process ID to the form web viewer in a query parameter.

With our workflows now linked, we can pass the newly edited Thing data back to our initial web viewer, which allows us to resize it or change its label as necessary giving the user feedback that the edit was successful. The animation below shows the workflow interaction during the edit process. Notice how the size of the Rice Cooker changes after the edit is complete.

Edit workflow
Two workflows interact when a Thing is edited (Click to open in a new tab)

Wrapping up

In this post we have shown how we can leverage the FileMaker platform to create dynamic forms and other UI elements. And we have defined a powerful workflow to underpin FileMaker web integrations.

I hope this post has been informative and inspiring. That being said, we have barely scratched the surface of what’s possible and readers may notice some gaps in the functionality of Stacked It, such as the inability to delete Things and the rather plain color picker that doesn’t show us a preview of the color we are selecting. Additionally, shape editing is not possible on iOS devices, and the app will not work in WebDirect.

The Stacked It example file provides the tools to help you fill some of these gaps, so feel free to give it a try and let us know how you get on. You can comment on the blog, reach us on Twitter, or contact us.

Leave a Reply