Expression Binding runScript vs. Property Change Script

I’m trying to run a script whenever a set of template properties are changed in order to delegate some of our more complex component theming to a script module. The obvious first choice was to use a property change script, except these don’t execute in design mode. The only thing I know that executes in design mode are expression bindings. By creating a new template property with an expression containing all of the relevant properties, I can get a runScript to fire anytime a property in the expression changes.

{Template.stringProp} +
binEnc({Template.boolProp1}, {Template.boolProp2}, {Template.boolProp3}, {Template.boolProp4}) +
runScript('global.path.to.class.instance.updateTheme(self)')

It works, but it seems weird. Also I lose access to event.propertyName in my script. I can be sure that at least one of the properties changed, but not which one exactly. This could be resolved by breaking things up into multiple runScript expressions but that seems like a slippery slope leading to an unmanageable amount of bindings.

Has anyone else been down this rabbit hole before?

Yes. Long ago. I created the objectScript() expression function in my Simulation Aids module to address the limitations of runScript(). I, too, like some scripting to always work in the designer.

FWIW, runScript() was later upgraded to include ‘self’ in scope of component bindings, and to pass extra arguments if the given python expression yields a function name. I got credit in the release notes. (:

Anyways, the remaining differences:

  • objectScript() can do extra arguments in a self-contained expression,
  • objectScript() exposes the binding for arbitrary re-triggering. (polling)
  • objectScript() exposes a long-lived state dictionary, handy for computing deltas and/or sums.
  • objectScript() can get the target property name (where applicable) from the binding.
1 Like

Note that objectScript's state dictionary could hold the prior values of the arguments, so you could tell which one(s) changed. (:

1 Like

Well this is pretty great! That basically resolves everything. :tada: :tada: :tada:

Side question: what cases come up where binding.childInteractionUpdated() is useful? Is there any advantage over using binding.target and system.db.refresh()?

I’m pretty sure system.db.refresh() is calling .childInteractionUpdated() under the hood. So you might as well call it directly. I’ve used it when I wanted to run a long calculation in the background and re-poll when completed. It is also used in my sample project’s animation. Unlike most polling, you can vary the intervals independent of the expression’s refresh. (You can change runScript’s poll interval at runtime, but the change is itself a trigger to re-execute the expression.)

Makes sense, just making sure there wasn’t any additional magic going on.