1 of 1 people found this helpful
Two techniques that i use:
First, I use a global variable to disable all triggered scripts such as $$TriggersOff
I write any script performed by a script trigger like this:
If [Not $$TriggersOff ]
#Rest of script goes here
Then in any script where I don't want triggered scripts to be performed if their trigger is tripped, I include two script steps:
Set Variable [$$TriggersOff ; Value: True ] --> Disables my triggered scripts
Do stuff here that might trip script triggers
Set Variable [$$TriggersOff ; Value: False ] ---> Re-enables them
You have to take care that all places where a script exits or halts uses a copy of the last script step to re-enable triggered scripts or your script will leave them disabled when you may not want them to be. And during development, I keep on hand a script that just sets the variable to null or False so that if I terminate a script in the middle or forget to put in the re-enabling script step , I have a quick way to re-enable triggers.
My second technique is to use scripted methods the minimize the type of layout interactions that trip script triggers. I can't always avoid them, but when I can, I do.
Example 1: Use Set Field instead of Insert Calculated Result. Set Field won't change the focus to the target field and thus won't trip OnObjectExit or OnObjectEnter.
Example 2: Use "pushback" relationships to create related records instead of changing layouts. This type of relationship links a global field in the layout's table to the primary key field in the related table with the "create" option enabled. You then can use set field to clear the global field followed by set field to set nearly any field in the related table in order to create a new related record. The new record auto-enters a serial number or UUID into the primary key field and it is "pushed back" into the global field where you can use it if needed or you can clear the global field in order to create yet another related record.
I don't mean to be critical; my wife says I'm anal...but...$Flag isn't a global variable as the comment text indicates; $$Flag is a global variable.
Whew, glad that's off my chest (yes, my wife is correct).
Now that that is passed I can move on. I'm extremely new to FMP so please excuse my ignorance; I don't understand "The most common is the unintended loop where the script calls or interacts with the field that caused the script to trigger. i.e. Self triggered loop."
Is self triggered loop a FMP term? Is this something like a date field that triggers a script that modifies the date field so it is triggered again, and again, and again...
Your code above ensures that, once encountered, the script ends; cool, but is this issue really a common occurrence?
I'm just trying to understand the problem (in case I run into it too).
Will you please provide an example of a Self Triggered Loop?
You have a dizzying intellect. I followed your first example (pretty simple concept) but your second left my head spinning .
Being new to FMP I am not familiar with global fields (I'm a MSSQL user). I bought 'the missing manual' but it's a slow read. I'd like to learn more...
The pushback relationship is something I learned right here in the forum. It's often used with the "connector" part of the Selector Connector data models.
Think of global fields as much the same as putting a Text box into the UI that is not tied directly to a field. A global field has no index and only one value no matter which record in its table is current. The value of a global field is accessible from any layout and any script in the file regardless of relationships that do or do not exist--so it's truly global within a given file.
In addition, when a file is hosted, each client gets their own "virtual copy" of that global. Changes that they make to a global field's value are not visible/accessible to other clients and vice versa.
This all makes Global fields vary useful as a UI tool.
Note that global variables such as $$TriggersOff are not a field and so cannot be directly edited for the user, but like a global field, their values are accessible from any context within the file in which it is created.
Thanks for the alternate approaches. My understanding of FM terminology is $Flag is defined as a "LOCAL VARIABLE" and is related to the running script only. The $$Flag designation is defined as a "GLOBAL VARIABLE" which is related to any script that may be exercised during the script chain. I was taught that neither are related to Global storage issues which are related to the definition of a field in a record. Global fields are available across all tables without relationships whereas other types of fields generally require relationships. The first is a script variable and the second is a field concept. Am I missing the boat once again?
What would you do if the script trigger was absolutely necessary for the package to function correctly. In this case, allowing the loop to occur but limiting it to one cycle is the only solution I have found to date. A bit of a klutz I admit but it works.
Global variables persist beyond the script chain. As long as the user has not closed the file; the values are still there.
A script (or on screen merge variable display; or portal filter based on the variable; or whatever) will still see that variable this afternoon or tomorrow if the user has not closed the file.
Apologize to the wife for me but the reference is to the storage area, "Book::Scratch1" field, not the $Flag variable.
I would venture to say that there is probably a trigger-less way to achieve what you want. But say that you absolutely must have a trigger then the solution lay in having that script not do anything that would cause other triggers. I know that's pretty much kicking-in an open door but it has to be said
Or set a flag to signal the other triggers not to do something but that in itself is a kludge. When you get to this point you are probably relying on event triggers more than you should...
There are many ways to skin a cat. If you need a cat skinned this is just one way to do it. The intent was to solicit other methods of cat skinning not to plumb the depths of the moral issues surrounding cat skinning. If you have never had this problem, then you are obviously much better at script design than me and that obliviously does not take much training.
That said, thanks Bruce for a very eloquent alternate approach. Does not solve my problem of needing the script to execute but limit its execution to only one loop but eloquent none the less.
Simplistic Example of Problem:
Make a new file 'ScriptTriggerLoop' with only one text field named Test.
Using Scripts Workspace, create a script called TriggerExample with the following two commands:
Go to Field [ScriptTriggerLoop:Test]
Perform Script ["TriggerExample"]
Go to layout mode and set the field Test, Script trigger mode to: OnObjectEnter 'TriggerExample'
Select OK, return to Browse mode, and your loop will start. You could add a couple of set field commands and pause commands between these two commands to better visualize the effect. I suggest using:
Set Field [ScriptTriggerLoop::Test; "Hold ESC to end"]
Pause/Resume Script [duration (seconds): .02]
Set Field [ScriptTriggerLoop::Test; "Click to start Loop"]
Pause/Resume Script [Duration (seconds): .02]
These lines slow down the action and give you visual as well as instructional information in the process. Save the script and open the file and watch the action. To stop the loop hold down the ESC key. To reset, click anywhere on the header or footer area of the layout. To restart the loop, click in the Test field
"That said, thanks Bruce for a very eloquent alternate approach."
I have not chimed in except to attempt to clarify global variable behavior.
You're that good Bruce ~ heh