16. Advanced Scripting Techniques

What Is Advanced Scripting?

Chapter 9, “Getting Started with Scripting,” presented an introduction to FileMaker Pro scripting techniques. It covered such topics as error trapping, linking scripts together via subscripts, conditional branching, looping scripts, and using custom dialogs. You should become familiar with all these essential scripting techniques.

This chapter explores several additional scripting techniques, including working with script variables, and script input/output techniques. Although we think that everyone can potentially benefit from learning these techniques, they do require a solid familiarity with general scripting techniques, calculation formulas, and the Relationships Graph. For this reason, we have opted to present these as advanced scripting techniques.

Image To put these topics together with advanced layout and calculation techniques, see Chapter 18, “Advanced FileMaker Solution Architecture.”

Script Parameters

FileMaker 7 introduced script parameters, a means of passing inputs into a script. FileMaker 8 added to the picture by adding script results, the capability for a script to output a piece of data after it’s finished executing. In FileMaker 9, an enhanced script debugger and a significant redesign of the script-editing interface brought everything together so that a truly powerful programming language is part of FileMaker.

The capability to move data in and out of scripts is desirable because it means that scripts can be written more abstractly and thus can be reused. By abstractly, we mean scripts written to solve general problems rather than specific ones. Using script input/output saves you time, reduces the number of scripts that are necessary in your files, and makes your scripts easier to maintain.

By abstracting the information, you can create a script that, for example, goes to a layout as specified by a script parameter. Such a script can replace a multitude of other scripts such as GoToCustomerLayout, GoToGroupLayout, and so forth. If you are converting an older FileMaker solution to a current version of FileMaker, you will probably find that you can remove a lot of scripts by converting them to use parameters (parameterizing them).

Much of what there is to say about script input/output applies equally well to script parameters (inputs) and script results (outputs). We discuss script parameters first and then delve into a consideration of script results.

Script Parameters

Before we get into the details of how and why to use script parameters, a short example will give you a concrete sense of what script parameters are all about and why you want to learn this information. Imagine that you want to create several navigation buttons that take users to various layouts. One way to do this is to create a separate script that’s hard-coded to go to a particular destination. You would need as many scripts as you have destination layouts, and every time you wanted to add a new destination, you’d create a new script.

Another way to accomplish this task is to create a generic Navigate script that navigates to a layout specified by the script parameter passed to it. Then, when setting up the buttons themselves, you would simply call the Navigate script, specifying the destination as the parameter. This approach has the advantage of requiring only a single script. To add another destination in the future, you simply specify the new destination as the parameter. There is no need to add a new script or to edit the original Navigate script.

It’s clear from this example that extracting hard-coded values from a script and placing them instead into script parameters has a tangible benefit. Keep this example in mind as you read further about script parameters.

Specifying Script Parameters

Script parameters can be set in several places: as part of a button definition, as an option for invoking a subscript within the Perform Script script step, or as part of the definition of a custom menu item. Figure 16.1 shows the first of these: the dialog for specifying which script should run when a button is clicked. The interface for specifying a subscript is the same; it, too, gives you a place to specify a parameter when calling a script.

Image

Figure 16.1. When attaching a script to a button, you can also specify an optional script parameter, which is passed into the script.

At the bottom of this dialog, you have the option to specify a script parameter. The parameter can be some text string you type into the space provided, or you can enter a calculation formula as the parameter. Clicking the Edit button brings up a standard calculation formula dialog. If you use a calculation formula as your script parameter, when the button (or subscript) is triggered, the formula is evaluated and the results are used as the parameter.

The actual string or formula you use as your parameter completely depends on what you’re trying to accomplish. Later in this section, you see some sample applications of script parameters.


Image Note

Only scripts launched by buttons or triggers, launched through Custom Web Publishing, or called as subscripts of other scripts can have script parameters passed to them. Scripts launched through the Scripts menu or as startup/shutdown scripts (under File, File Options) cannot have script parameters passed to them.


Retrieving a Script Parameter

The Get(ScriptParameter) function can be used to retrieve the value of the parameter passed to a script. If no parameter was specified, this function simply returns an empty string. The value of the script parameter can be accessed anywhere from within the script in this way. It can’t be changed or altered in any way, and it expires as soon as the script is complete.

Any subscripts called by a script can have their own independent script parameters; they do not inherit the parameter of the script that calls them. As an example, say that the string abc was designated as the parameter to be passed to a script called Main Script. Assume further that Main Script called a subscript called Child Script as its second step, and that the parameter xyz was specified as part of the Perform Script step. Within Main Script, Get(ScriptParameter) always returns abc. Within Child Script, Get(ScriptParameter) always returns xyz.

The parameter passed to a script can be the result of a calculation, so by using Get(ScriptParameter) as the script parameter, as shown at the bottom of Figure 16.1, you can pass a script’s parameter down to the subscripts it calls.

Passing Multivalued Parameters

The interface for specifying script parameters allows only a single value to be passed to a script. For many situations, that is sufficient to achieve the desired outcome. Other times, however, you will find that you want to be able to pass multiple parameters to a script. Although this isn’t directly possible, there are several methods to achieve such a result.

Parsing a Text Array

The simplest way to pass multiple values in a script parameter is to specify a delimited array as the script parameter. For instance, if you wanted to send a parameter that contained the values Fred, 123, and Monkey, you could send the string Fred|123|Monkey, or even Fred+123+Monkey.


Image Note

The delimiter you use (here we’ve used pipe characters and carriage returns) is up to you; just choose something you know won’t be found in the data you’re passing.


To retrieve a portion of the passed parameter, use the built-in text-parsing functions of FileMaker Pro. If you’ve used carriage returns as your array delimiter, the GetValue function is the easiest way to extract a particular value. Say that you want to grab the third value from within your script, any time you wanted access to this value, you would use the following formula:

GetValue (Get (ScriptParameter) ; 3 )

Image For more details on text-parsing functions, see Chapter 8, “Getting Started with Calculations,” and Chapter 15, “Advanced Calculation Techniques.”

The nice thing about using delimited lists to pass multiple values is that you can set them up easily. Even if some of the values are derived as calculated results, it’s still quite easy to set up a formula that concatenates all the appropriate pieces together. For instance, if you wanted to pass the current layout name and the current time as the two values of your script parameter, you would use the following formula:

Get (LayoutName) & ¶ & Get (CurrentTime)


Image Note

The main drawback of this method is that the burden is on you, the developer, to know what each position in the array represents. There’s nothing in the parameter itself that offers any assistance. This can (and should!) be clarified with script and/or calculation comments.


Using the Let Function

Another method for passing multiple values in a script parameter involves the Let and Evaluate functions. If you have a good understanding of those functions, you’ll likely appreciate the elegance of this technique.

Image For more information on the Let and Evaluate functions, see Logical Functions,” p. 407.

Imagine that you pass as your script parameter the following string:

"First Name = "Fred"; Favorite Number = 123 ; Favorite Animal = "Monkey""

What you have here is a set of name/value pairs, separated by semicolons. Immediately you can see one of the benefits of this method over the previous one: When you pass both names and values, the parameter becomes more meaningful. In six months when you need to troubleshoot or enhance your script, you won’t have to rack your brain to remember what the elements in your parameter represent. Another benefit of this method is that the order of the values doesn’t matter. They’re retrieved by their name rather than by their position within the parameter.

You’ll notice that within the parameter are backslashes before all the internal quotation marks. This process, known as escaping your quotes, is necessary any time you want to pass a string that contains internal quotation marks. For this technique, you need to escape the quotation marks surrounding any text values in your parameter; numeric values (such as the 123) do not need quotation marks and hence don’t need to be escaped.

You might recognize that the parameter specified previously is structured similarly to the first parameter of a Let function. This isn’t a coincidence. Recall that the Let function allows you to set variables within a calculation formula. Imagine you had the following formula:

Let ([First Name = "Fred";
  Favorite Number = 123 ;
  Favorite Animal   "Monkey"
  ]
  ;
Favorite Animal)

This formula sets three variables (First Name, Favorite Number, and Favorite Animal) and then returns the value of the Favorite Animal variable. It would, in fact, return Monkey.

By combining the Let and Evaluate functions, you can build a formula that pulls out a named value from within a script parameter. The Evaluate function executes a dynamically constructed calculation formula. Therefore, within your script, any time you want to retrieve the value of the variable Favorite Animal, you would use the following formula:

Evaluate ( "Let ([" & Get(ScriptParameter)  & "]; Favorite Animal)")


Image Tip

If you foresee a need to do much parsing of multivalued script parameters, you should consider creating a custom function to simplify the process even more. That way, you won’t have to remember the syntax for the Let and Evaluate functions every time you need to retrieve a parameter value. Figure 16.2 shows the definition for a custom function called GetParam.


Image

Figure 16.2. The custom function GetParam abstracts the script parameter-parsing routine even more.

As you can see, a string containing a Let function is dynamically assembled from the value of the script parameter. The Evaluate function is used to execute it. To return one of the other variables within the script parameter, you would simply need to change the end of the formula to reference the proper variable name.

Image You need FileMaker Pro Advanced to create custom functions. For more details on creating custom functions, seeCustom Functions,” p. 427.

The GetParam function takes a single argument, paramName. The formula for the function is the same as the Evaluate formula shown previously, but with the paramName inserted in the place of the hard-coded parameter name:

Evaluate ( "Let ([" & Get(ScriptParameter) & "]; " & paramName & ")")

Now, within your script, to retrieve the value of the variable Favorite Animal, you just need the following formula:

GetParam ("Favorite Animal")

This final abstraction provided by the GetParam custom function certainly makes the parameter parsing more convenient. After it’s in place, you can pass and retrieve multivalued script parameters with ease.

Passing Structured Data Elements in the Style of XML

The final method in this discussion for passing multivalued script parameters involves creating your own structured data elements. It’s really a hybrid of the other two methods in that it requires standard text parsing to retrieve an element (like the first method), but the elements are meaningfully named (as in the second method).

The syntax you create for naming elements is up to you. We generally prefer an XML-like structure because it’s easy to use and organize. For instance, to pass the same three values discussed in the preceding section, you might specify the following as your script parameter:

"<First Name>Fred</First Name><Favorite Number>123</Favorite Number><Favorite
Animal>Monkey</Favorite Animal>"

This is, of course, simply another way of specifying element names and values. But you don’t need to worry about escaping any quotation marks, as you do with a string that will be used in an Evaluate statement. To retrieve the value of a particular element of the script parameter, you would need to use standard text-parsing functions. This is best accomplished with the creation of a custom function; you then need to write the parsing logic just once. The following formula could be used as the definition for such a custom function; the function’s only parameter is paramName:

Let ( [
    openElement = "<" & paramName & ">";
    closeElement = "</" & paramName & ">" ;

    startPos = Position (Get(ScriptParameter) ; openElement ; 1; 1) + Length (openElement);
    endPos = Position (Get (ScriptParameter) ; closeElement ; 1; 1)] ;

Middle (Get(ScriptParameter) ; startPos ; endPos - startPos)
)

If this function were called GetParamXML, the value of one of the script parameter elements could then be retrieved with the function GetParamXML("First Name"). The custom function is hard-coded to parse out a value from a script parameter.


Image Tip

Andy Knasinski has an XML-parsing custom function posted at www.briandunning.com/cf/1 that you can explore.


Strategies for Using Script Parameters

Using script parameters can greatly reduce the number of scripts in a file and can make your database much easier to maintain. You should consider using script parameters in several common programming scenarios.

Modularizing Scripts

The first—and most important—reason for using script parameters is to add a layer of abstraction to your scripts, thereby making them more modular and reusable. Instead of writing scads of single-purpose scripts, if you can generalize your scripts by using script parameters, you will need fewer scripts and your solution will be easier to maintain.

You will know if you have encountered a situation that can potentially be simplified and strengthened by using script parameters if you find yourself writing several scripts that do basically the same thing, differing only in some specific value. In place of that specific value, use Get (ScriptParameter) and then have the buttons or other scripts that trigger the script specify the particular value.

For example, say that you’ve developed a system that contains a calendar and that one of your layouts shows all the scheduled appointments for a given week. You’d like to be able to place a button above each of the seven days of the week (Sunday through Saturday) that users can click when they want to create a new appointment on that particular day. Assume that you have a field that contains the date of the Sunday of the week. Therefore, a script that would create a new appointment on Wednesday would do something like the following:

New Record/Request
Set Field [Appointments::AppointmentDate ; SundayDate + 3]

The scripts for creating appointments on the other days of the week would differ from what’s shown in the preceding formula only by the constant that’s added to the SundayDate. You could therefore write seven scripts, link them to your buttons, and move on to your next task.

We hope you can already see how and why script parameters can be used here. In the sample script, if you change the + 3 to + Get (ScriptParameter), you need only a single script to do the work of the seven required without script parameters. Each of the seven buttons calls the generic version of this Add Appointment script, passing as a parameter an integer from 0 to 6 to differentiate them from each other. By using this method, you replaced seven hard-coded scripts with a single generalized one.

Passing Data Between Files

Another situation in which script parameters can be beneficial is for passing data between files. Using script parameters for this purpose saves you from needing to create extra fields and relationships in your files.

As an example, imagine that you have a file called Transactions and another called TransactionArchive (each with a single table with the same name as the file). You periodically archive old transactions into the archive file, but occasionally you need to pull a record back from the archive into the main production file. Further, you’d like to avoid placing a table occurrence from the archive file in the main file because the two need to be able to function independently.

Because you can call scripts in another file without having a relationship to that file, script parameters make an ideal transfer mechanism for moving data between unrelated files. In the sample scenario, you might set up a script in the TransactionArchive file that calls a subscript in the Transaction file, passing a multivalued parameter (using one of the methods described in the preceding section) that contains the pertinent data elements from the transaction. In the Transaction file, then, your subscript would create a new record and populate it using the parsed-out parameter data.

In this example, importing the record from one file to the other would have been another solution within the defined constraints. Nonetheless, this example still clearly demonstrates the role that script parameters can play in moving data around. It’s certainly preferable to copying and pasting data, or even parking data in global fields for later retrieval (both of which were common techniques with versions of FileMaker before version 7).

Script Results

Script results are, if you like, the flip side of script parameters. A script parameter lets you feed data into a script; a script result lets you pass data back out of a script. In the past, you might have done this by putting some data into a global field or a global variable for other scripts to look at later. But the best choice now is generally to use a script result.

Using a script result is the best choice because the script result is not stored automatically unless you choose to do so. Thus, a subscript can return a value, and the calling script can test that value and decide what to do next. If you use a global variable or global field, the value persists after you test it. In the future, you or someone else might look at the global field or variable and use its value for an unrelated purpose, possibly jeopardizing the logic of the script that relies on it.

To return a result from a script, you use the Exit Script script step to specify a result to return when the script exits. Much as when specifying the value for a Set Field or Set Variable script step, you can create a calculation expression that defines the result to return.

That takes care of how to return a script result. To access the returned result, you have to use the Get(ScriptResult) function, a sort of twin to Get(ScriptParameter). Get(ScriptResult) hands back whatever result was returned by the most recently completed script or subscript.

Let’s consider a full example. As we’ve suggested, one of the main reasons to use script input/output is to increase the reusability of your scripts. Consider a solution with a large number of reports. When you allow users to print reports, it’s common to display the report in Preview mode first, pause the script, and then, on resuming, pop up a dialog box asking whether the user wants to print the report. The task of prompting the user for print confirmation might happen over and over again in a report-intensive solution. Using script results, you can write a single script to query the user and then return the user’s choice as a script result. Here’s what such a script might look like:

Show Custom Dialog [ Title: "Print Confirmation"; Message: "Would you like to print the report?"; Buttons: "Yes", "No" ]
Exit Script [ Result: Let (
        [
        msg = Get(LastMessageChoice) - 1;
        choiceText = Choose ( msg ; "Yes"; "No" )
        ];
    choiceText ) ]

Notice the difference in the Exit Script step. As part of this step, the script specifies that a calculated result be returned from the script. The calculation looks at the numeric result of the dialog box choice, converts it into text using the Choose function, and returns the corresponding text result.

To use this script’s modular functionality, another script has to call it. A script to display and optionally print a single report might look like this:

Go to Layout [ "Report" ]
Sort Records [ Specified Sort Order: Reporting::Region; ascending ] [ Restore ]
Enter Preview Mode
Pause/Resume Script [ Indefinitely ]
Perform Script [ "Print Confirmation Dialog" ]
If [ Get ( ScriptResult ) = "Yes" ]
    Print [  ] [ No dialog ]
End If
Go to Layout [ original layout ]
Enter Browse Mode

This script performs all the usual sort of management common to previewing reports: navigating to a layout, sorting the records in some way, entering Preview mode, pausing for the user to look over the report. When the user resumes the script, though, the script goes straight into the confirmation subscript. Thereafter, the outer script uses Get(ScriptResult) to determine the result of the confirmation dialog and then prints the displayed report, or not, accordingly.

With such a script, instead of having a dozen print dialogs coded all over your system, you now have just one. If a user reports a problem with the print dialog, you now know where to start looking. And any changes or improvements made to the print confirmation process immediately benefit all reports that use this functionality.

Final Thoughts on Script Input/Output

The script input and output capabilities of FileMaker Pro represent a major advance in the capability to construct streamlined, reusable routines within a FileMaker solution. Mastering the use of these techniques is critical to getting the most out of FileMaker Pro. We recommend that you study these features carefully and that you aggressively look for opportunities to use them. Any time a script does similar work with different input values, consider using script parameters. Any time a script might be better structured as a tool that does some work and then reports on the results, consider reporting those results via a script result. Your solutions will become cleaner, simpler, and more elegant.

Script Variables

If you’ve worked with other languages or development environments, you’re familiar with a variable as a type of named, temporary storage. For example, in the PHP programming language, you might write this:

$x = 7;
$y = 9;
$z = $x + $y;

Here $x, $y, and $z are all variables—temporary storage elements to which values are then assigned (the $ in PHP indicates that these are variable names, and FileMaker uses a similar convention for variable names). They contrast with the permanent storage of fields in databases. In later expressions, the variable names stand in for the values stored in them. So, you’d expect that when the preceding program runs, the variable $z will end up storing a value of 16.

Often, as you build up a program or routine, you’ll find yourself wanting to rely on named, temporary storage elements like these. In previous versions of FileMaker, the only place to cache such data was within FileMaker’s database structures, by putting the data into one or another kind of field.

Consider the simplistic example of a script that beeps ten times in succession. Previously, you might have defined a field with global storage to act as a counter and written the script like this:

Set Field [Loop::gCounter; 1]
Loop
    Beep
    Pause/Resume Script [Duration (seconds):1]
    Set Field [Loop::gCounter; Loop::gCounter + 1]
    Exit Loop If [Loop::gCounter > 10]
End Loop

This approach has always worked fine, but it has some drawbacks:

• Even though the counter field is used only in this script, it has to be defined within the field definitions for the table as a whole. It will always be there, cluttering up the list, even though it might apply to only a single script.

• The storage is not as temporary as you would like. The field gCounter goes on holding its value and being accessible after the script completes. This is one reason you need to reset the field to 1 at the start of the script. If the script has run previously, it might still have its old value of 11, or it might have some other value altogether if someone edited the field directly and stored it in the database.


Image Note

If you are working with a FileMaker database that has its roots in the past, you might encounter a number of global fields. Converting them to variables is a routine part of conversion to the current versions of FileMaker; doing so can make the databases smaller and the scripts simpler and most robust. One of the great recommendations of variables is that they are temporary, so they cannot hang around to take up storage and confuse future developers who work on the database.


About Local Variables

A local variable is one that exists and has meaning only within a single script—exactly what you want for the loop-counting example shown previously. If you were to rewrite the looping script using script variables, it might look like this:

Set Variable [$counter; Value:1]
Loop
    Beep
    Pause/Resume Script [Duration (seconds):1]
    Set Variable [$counter; Value: $counter + 1]
    Exit Loop If [$counter > 10]
End Loop

Local variables are named using a single dollar sign ($), and they’re created and manipulated using the Set Variable script step. Figure 16.3 shows the options for the Set Variable script step.

Image

Figure 16.3. The capability to set variables is a powerful feature in FileMaker Pro.

You’ll notice that the Set Variable script step enables you to set the variable’s value dynamically, using the Specify option. This means that a variable can be used to hold the results of any expression you can define using FileMaker’s Calculation dialog. You could store the current username, the current date, or the results of an expression such as $counter + 1. You’ll note also, by the way, that variables can be referenced from within such calculations just by using the variable name. For example, the following are perfectly valid calculation expressions:

$counter + 1

"Name: " & $userName

If a variable of the specified name is not currently defined, a reference to the variable returns an empty string (or a null result). The first expression of the preceding two would give a result of 1, whereas the second would give a result of "Name: " if the respective variables were not defined.

About Variable Scope

Variable scope is the area in which a variable has meaning and can be accessed. Variables in FileMaker have one of two kinds of scope: either local scope or global scope.

So far, the variables we’ve examined have local scope. It’s most common to refer to them as local variables. A local variable exists and has meaning only within the context of a single script. Consider the example of the $counter variable discussed previously. This variable exists only within the script in which it’s defined and manipulated. After the script completes, the $counter variable is undefined and cannot be accessed. Likewise, if you called a subscript from within that script, the subscript would not have access to the value of $counter contained in the parent script.


Image Tip

Because local variables exist only within the context of a single script, this can lead to subtle confusion. Consider a parent script that uses a $counter variable that then also calls a subscript. If you were to try to access the value of $counter within the subscript, you’d get a null value because you’d be trying to access a variable that had never been set within the context of the subscript. And if you were to try to set the value of $counter within the subscript by using the Set Variable script step, this would create a new variable, local to the subscript, with the same name, $counter. There would consequently be a total of two $counter variables: one local to the parent script and one local to the subscript. The two exist simultaneously and independently; they don’t conflict, and they don’t affect one another.


Local Variables Summary

So, let’s summarize what has been said about local variables:

• They are set using the Set Variable script step.

• They must have names beginning with $.

• They can be referenced within calculation expressions.

• They are limited in scope to the script in which they are defined (via the Set Variable script step). Neither subscripts nor parent scripts can access the value of a local variable.

• They do not appear in the Manage Database dialog.

About Global Variables

Global variables, denoted with a double dollar sign ($$userName, $$currentLayout), share many features with local variables. The only difference is in their scope. Whereas local variables are limited in scope to a single script, global variables retain their value no matter what script is running or whether a script is running at all. They can store values that persist across any or all scripts for the duration of a user’s session.

The last point bears repeating. Whereas local variables have script scope, meaning that they are limited in scope to a single script, global variables have file/session scope. Like globally stored fields, global variables are unique to an individual user: Each user has his own copy of a global variable, so the variable $$userName can have a different value for each active user. In addition, global variables cease to exist when a user session ends. If you work with a global variable, quit FileMaker, and then open the same file again, the global variable will disappear, until some logic in the files creates it again.

Global variables also have scope only within a single file. There is no way to “reach across” to pull a global variable’s data from one file into another. Such a thing is, by contrast, possible with globally stored fields. Global variables from other files cannot be accessed via relationships because they don’t appear in the field list.

So, what good are global variables? When does it make sense to use them? We recommend that, by and large, you use global variables for user session data: data specific to one user that is intended to persist for just that user session. Examples include the name of the currently logged-in user, user preferences such as a user’s chosen default layout, or any other user-specific data you might be storing, such as a user department or sales region—particularly when this information is not available from a database.

Global variables cannot completely obviate the need for globally stored fields. Globally stored fields have several capabilities not shared by global variables:

• Globally stored fields can be accessed across files by using relationships.

• Globally stored fields can accept user input.

• Globally stored fields can be used to drive relationships.

• Globally stored fields can be used to store the content of an input field from a custom dialog.

For example, if you were implementing a filtered portal (a portal whose contents change in response to user input), you would have to use a globally stored field to do so, both because you would need to capture user input and because you would need to use that input to drive the portal relationship.

Other Ways to Work with Variables

When you’re first starting out with variables, we recommend you try to stick to the following precepts until you feel you’ve mastered the basics:

• Use local variables for temporary storage used within the context of a single script.

• Use global variables to store user-specific session data (with the exceptions noted in the next point).

• Use globally stored fields, not variables, to store user-specific session data that must be captured directly from the user, must drive a relationship, or must be shared heavily across files.

Now that we’ve said all that, if you have mastered the basic concepts of variables, there are some advanced points to be made about them.

About Dynamic File Paths

There’s another nice feature of variables in FileMaker Pro that’s very much worth mentioning. Certain script steps, such as Export Records, as well as the Save Records as Excel/PDF script step, allow you to specify the location of an output file by typing in a file reference. In FileMaker Pro, that file reference can be taken from a variable, rather than being hard-coded.

If such usefulness isn’t obvious, let it sink in for a moment. In the past, it wasn’t possible to create names for exported files on the fly: You either had to let the user enter a filename or had to hard-code a single specific filename into the script step. If you wanted to name exported or saved files dynamically (say, you wanted to include the current date in the filename), you were out of luck, unless you chose to use a third-party plug-in.

To save files to a dynamically specified file path, you need to create that file path in your script and put it into a variable. (The path begins with the user’s desktop, so that is where the file is placed.) That variable can then be used in specifying a file path, as the following script example illustrates:

Go to Layout [ "Contacts" ]
Show All Records
Set Variable [ $filePath; Value: Let (
  [
    theDate    = Get(CurrentDate);
    theYear    = Year(theDate);
    theMonth = Month(theDate);
    theDay     = Day(theDate);
    dateText  = theYear & "_" & theMonth & "_" & theDay;
    filePath    = Get ( DesktopPath ) & "Export_" & dateText
  ];
  filePath ) ]
Save Records as PDF [ File Name: "$filePath"; Records being browsed ]

Viewing Your Variables

One final note on variables in FileMaker Pro. We’ve made the point a few times that variables are beneficial in that they don’t add clutter to the database schema: They don’t appear in the Manage Database dialog, nor do they appear in the field lists that go along with operations such as Sort or Import Records. There’s a disadvantage to this as well: There’s currently no way to see a list of all the variables currently active in a FileMaker solution.

It is possible to view the values of individual variables in the FileMaker Pro Advanced Data Viewer, but you must enter the variable names one at a time, as with any other expression.

FileMaker Extra: Recursive Scripts

Chapter 15 discusses how you could make custom functions recursive by including calls to themselves within their formulas. In a similar manner, you can use script parameters to create recursive scripts. Although this isn’t something you need to do on a daily basis, there are some interesting applications for recursive scripts.

A recursive script is one that calls itself repeatedly until some exit condition is satisfied. Each time the script calls itself as a subscript, it passes a script parameter that can be used as part of an exit condition test. In many ways, recursive scripts are quite similar to looping scripts, and many tasks you can accomplish with one can be done as easily by the other. As an example of a recursive script, consider this Recursive Add script:

If [Get (ScriptParameter) >= 100]
    Exit Script
End If
New Record/Request
Perform Script ["Recursive Add"; Parameter: Get (ScriptParameter) + 1 ]

This script adds 100 new records to the current table. It’s first called without a script parameter, so the first time through, the script calls itself as a subscript, passing a parameter of 1. The parameter increments each subsequent time through until eventually the exit criteria (Get (ScriptParameter) >= 100) are met.

If there are any steps in the script after the recursive subscript call, they are all executed, from the inside out, after the exit criteria have been met. Try to predict what would happen if you added the following steps to the end of the preceding script:

Beep
Show Custom Dialog ["The parameter is:" ; Get (ScriptParameter)]

The 100 records would be created exactly as they were originally. But after they were all created, you would hear a beep and see a message telling you that the script parameter value is 99. After clicking OK, you would then hear another beep and see a message telling you that the parameter is 98. This would continue for some time, and eventually the last message you’d see would be that the parameter is empty, which, of course, was the condition on the first trip through the script.

As a final example of recursive scripting, consider the following script, which flags duplicates among a set of records. Assume that the set contains a list of names, which has been sorted by name before this script is called:

If [IsEmpty (Get (ScriptParameter))]
    Go to Record/Request/Page [First]
Else
    Go to Record/Request/Page [Next; Exit after last]
    If [Get (ScriptParameter) = Contacts::Name]
        Set Field [Contacts::DuplicateFlag; "Duplicate"]
    End If
End If
Perform Script ["Mark duplicates"; Parameter: Contacts::Name]

During each iteration through the script, the current record’s name is compared against the value of the script parameter, which was set to the value of the previous record’s name. The exit condition here is the Exit after last option on the fourth line; the script continues through the set of records, stopping only when there’s no next record to go to.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.197.213