If your web viewer code calls the FileMaker.PerformScript function when loading, it’s possible that it hasn’t been injected into the web viewer yet, and your code will fail with a breaking error. How can we harden our opening routine to ensure this breaking error doesn’t occur and provide the best experience for the user?
Part 3 of 3 of our series on Web Viewer Tips for FileMaker 19.
In a previous post on Web Viewer Tips we discussed how the new version of DayBack uses a familiar JavaScript and API pattern and the benefits of doing this. In this post, we’ll suggest bringing another common JS pattern into your app: try-catch.

“Can’t Find Variable: FileMaker”
The web viewer will initiate making calls to the FileMaker file via the FileMaker.PerformScript function as the web viewer loads, but since FileMaker is also injecting the FileMaker.PerformScript function into the web viewer as it loads, it’s possible that the function hasn’t been injected before your code calls it. This is part of the asynchronous nature of JavaScript, sometimes the FileMaker.PerformScript function is there in time, and sometimes it isn’t. When it isn’t there, the error “Can’t find variable: FileMaker” is thrown. This is a breaking error, and your JavaScript will stop executing on the offending line.
In most situations, we can trap for this kind of error by testing for it in a simple If statement, something like:
If ( FileMaker && FileMaker.PerformScript ) { FileMaker.PerformScript ( 'some script' , 'some parameter'); }
However, this does not help and you’ll still get the “Can’t find variable: FileMaker” error even when referenced in the if statement and the code breaks.
Using Try-Catch Blocks
Using Try-Catch blocks is a more robust way of trapping for errors and exceptions in JavaScript. All code run within the Try block will complete even if there is a potentially fatal error. Instead of the error being reported at the browser level, it will be sent to the Catch block as its argument. Here’s an example where the alert function is misspelled.
try { alret(‘Hello World’); } catch(e) { console.log(e.message); }
If the “alret” function is called outside of the try block, then this error would break our code and halt its execution at the line. By wrapping it in the try block, the code will continue safely and pass the message to the console log.
Since we know the FileMaker.PerformScript function will eventually be injected into the web viewer, we’re going to use the try-catch blocks to simply retry the routine a few times so that the injection can catch up with our code. This is the function we came up with, and even though it’s only on web viewer load that we’re worried about the failure, for consistency’s sake, we use it for all our calls to the FileMaker file.
function fileMakerCall(payload, retry) { //payload argument contains the script name var script = payload.script; //initialize the retry argument if empty retry = retry ? retry : 0; //number of times to retry before reloading the page //we've only observed the need for one retry befor FileMaker.PerformScript is there var numRetries = 5; //stringify the JSON payload for fileMaker var payloadString = JSON.stringify(payload); //begin try-catch try{ FileMaker.PerformScript(script,payloadString); } catch(e){ //try has failed re-run on a half second timeout if it hasn't been attempted too many times //enable for testing and debugging //console.log(e); if(retry < numRetries){ setTimeout(function(){ fileMakerCall(payload,retry); },500); } else{ //too many retries, reload the whole page //although we've never needed this location.reload(); } } }
As you can see, the function will try to call FileMaker.PerformScript again up to 5 times. We don’t want to get stuck in an infinite loop, so providing a maximum number of retries is certainly a best practice, although I’ve never seen more than 1 retry be required for the FileMaker.PerformScript to be there and for the try block run without passing the error to the catch block. At the worst, users may experience a half-second delay during the loading routine instead of simply failing and needing to manually reload the web viewer.
Conclusions
The asynchronous nature of JavaScript can create timing issues. Although this error may seem particular to FileMaker and its web viewer, similar issues have existed for web developers for years: so have the solutions they’ve come up with to handle them. JavaScript may feel new to many of us in the FileMaker world, but we can take comfort that there’s a wealth of experience out there that we can adapt to our solutions. If you encounter similar JavaScript issues with your web viewer apps, even though the exact terminology may be different from what we’re now doing in FileMaker, someone has likely solved the issue. Some googling and searching through Stack Overflow will likely provide the solution.
10 Comments
Thank you so much!
After hours of frustration, this function saved the day.
Don’t know why, but FM 19.6.3 persistently failed to inject it’s code on even the simplest WebViewers today and this solution was the only one I’ve found that worked reliably.
I just found your article because I’m having a similar issue. Thanks for sharing your solution. Very helpful!
Unless I’m missing something, however, you never increment your retry counter. So if the call fails, it actually does create an infinite loop. You’d have to add the “+1” in the call:
setTimeout(function(){ fileMakerCall(payload,retry+1); },500);
Thank you, Max! In all our testing, we never saw it retry more than once, which is probably why we missed this. Much appreciated.
This also works:
var Defer = []; document.addEventListener(‘DOMContentLoaded’, function() { while (Defer.length) Defer.shift().call(); });
…
FROM:
https://stackoverflow.com/questions/41394983/how-to-defer-inline-javascript
Hello Jason,
Thanks very much for this post, and the code! please note that there is small spelling error on line 25 that breaks the script and throws a console error: “ReferenceError: Can’t find variable: setTimeOut”.
To fix it, just need to change “setTimeOut(function()” to “setTimeout(function()” in line 25.
Best regards,
Andrés
Thanks, Andrés! (changed)
Hi, using the ‘defer’ option for your javascript part where you assume the ‘FileMaker Javascript’ is injected did the trick for me.
https://www.w3schools.com/tags/att_script_defer.asp
Kind regards,
Jan
Thanks for sharing that Jan, that’s great to know!
Thanks for the knowledge Jason! I’ll be using the wrapper in my own stuff.
That’s great to hear Matt, thanks!