15. FR Toolbox Reference

https://paper.dropbox.com/doc/15.-FR-Toolbox-Reference--BtPUh~RQmGhnVhxrmMtUb0GUAg-sE8bOT9P8vdXUmPu91t3E

15. FR Toolbox Reference

<— Reactor Home

The following lists the suite of FRToolbox functions you can use for communicating with your FileMaker database.

The first section provides an introduction to management of a BlackBox in Reactor.

  1. Reactor Tag

  2. FileMaker calculation

  3. Get parameter Value

  4. Get value of field parameter

  5. Get table occurrence of field name parameter

  6. Get ‘where’ clause based on relationship

  7. Get list of initial creation values based on relationship

  8. Get container field images from container field name parameter

  1. FRToolbox Requests

    1. Find records

      1. Constrain records

      2. Send request

      3. Poll request

    2. Create record

    3. Update records

    4. Delete records

    5. Calculation request

    6. Perform script request

    7. Request configurations

  2. Utility methods

    1. Set default database

    2. FRToolbox version

    3. Debugging

    4. Date/time conversion

    5. Get field and table occurrence

  3. Reactor Actions

Reactor Tag

<?reactor reactor?>

Everything between these Reactor tags is interpreted by Reactor. This can be:

FileMaker calculation

You can perform any FileMaker calculation inside reactor tags

next_week_date_var = '<?reactor Get ( CurrentDate ) + 7 reactor?>';

Example output:  next_week_date_var = '22/06/2019';

Get parameter value

You can access the value of any BlackBox parameter as a local variable:

text_field_var = '<?reactor $text_field reactor?>';

Example output: text_field_var = 'mytext';

Get value of field name parameter

field_name_var = '<?reactor bbdev_Field( $field_name ) reactor?>';

Example output: field_name_var = 'Description'

Get table occurrence name of field name parameter

field_name_TO_var = '<?reactor bbdev_TO( $field_name ) Reactor?>';

Example output: field_name_TO_var = 'Task';

Get ‘where’ clause based on relationship from current context to table occurrence:

(This is generally used when querying data)

where_clause_var = '<?reactor bbdev_Relationship( bbdev_TO($field_name) ) reactor?>';

Example output:  Task.status = 'active' and Task.type = 'booked' 

Get list of initial creation values based on relationship from current context to table occurrence

(Generally used when creating a new record, in conjunction with the FRTB.create() method)

creation_values_var = '<?reactor bbdev_relationshipKeyBuilder ( bbdev_TO($field_name) ) reactor?>';

Example output: 

Names="status,type" Values="active","booked"

Get container field images from container field parameter value

<?reactor bbdev_LoadImages( $container_field ; $field_name ; $com.reactorize.env.loadedfilepath ) reactor?>

Example output:

<img src="task_01.png" />

<img src="task_02.png" />

<img src="task_03.png" />

(The $field_name values determine the filename they’re given.)

FRToolbox Requests

The following JavaScript methods can be used to perform requests on your FileMaker database and process the response. 

These requests can be chained together, and in many cases they must be. For example, all the following methods must invoke the send(); method to actually submit the request.

Find records

FRTB.find(field1, field2, field3, etc)

Returns a found set of records, the values returned depends on the fields specified in the request.

For example:

QueryData = FRTB.find(

  "Task::name",

  "Task::description"

);

Will create a request to retrieve the name and description field values from all records from the table that the Task table occurrence is based on.

If you have used the bbdev_Field() and  bbdev_TO() functions to store the field name(s) and table occurrence(s) into variables, you can can use those:

QueryData = FRTB.find(

  TO_name_var + "::" + name_field_var,

  TO_name_var + "::" + description_field_var

);

 

Constrain records

.where(whereclause)

Constrains a find request based on an SQL WHERE clause.

For example:

QueryData = FRTB.find(

  "Task::name",

  "Task::description"

).where(Task.status = 'active');

Will create a request to retrieve the name and description field values from all records from the table that the Task table occurrence is based on, that have a value of active in the Task::status field.

If you have used the bbdev_Relationship() function to form a ‘where clause’ you can use the .filter() method instead, which allows you to use FM-style syntax rather than SQL-style syntax (eg, Task::name rather than Task.name)

.filter(where_clause_var);

Send Request

This is used to actually submit a request to Reactor. You can first form the request and then send:

QueryData = FRTB.find(

  "Task::name",

  "Task::description"

).where(Task.status = 'active');

QueryData.send();

Or send it as you generate the request:

QueryData = FRTB.find(

  "Task::name",

  "Task::description"

).where("Task.status = 'active'").send();

This will submit your request to Reactor. However, to take a look at what comes back you’ll want to use a callback function. You can either define a function and call that:

function CheckResponse(response) {

  alert ( "Response received!") ;

}

QueryData = FRTB.find(

  "Task::name",

  "Task::description"

).where("Task.status = 'active'").send(CheckResponse);

Or you can define an anonymous function inside the request:

QueryData = FRTB.find(

  "Task::name",

  "Task::description"

).where("Task.status = 'active'").send(function(response) {

  alert ( "Response received!") ;

});

When you set a callback function with send(), it will include a parameter containing the response of the request. When using with a find() request, this response will include data retrieved.

The response of a find() request is returned as an object. This object contains a property called data. This data property is an array of objects. The key of each element of the array is the full field name (including table occurrence name). 

So in your callback function, to access the values of each record in the response, you’d loop through them:

for ( var i=0; i < response.data.length; i++) {

  taskname = response.data[i]["Task::name"];

  taskdescription = response.data[i]["Task::description"];

  alert (taskname + ": " + taskdescription );

}

Poll Request

This is used for polling for changes after the find() request is submitted.

Polling utilises a Timestamp auto-enter with a fixed name of zModID:

If you invoke this request, it will poll for changes after the data is queried; it can poll for new records, for changed records, or for deleted records.

QueryData = FRTB.find(

  TO_name_var + "::" + name_field_var,

  TO_name_var + "::" + description_field_var

).filter(where_clause_var).poll(PollChanges).send(function(response) {

  alert ( "Response received!") ;

});

This will call the PollChanges() function whenever the data is polled. By default it polls every 5000ms (5 seconds), or you can set your own interval if you wish:

.poll(PollChanges,2000)

This will set the polling to occur every 2 seconds.

When the PollChanges() function is called, it returns an object containing the poller results. The object contains three properties, each of which is an array of rowid values that point at records which have changed. The rowid is an internal FileMaker ID for each record.

So your polling function will look something like this:

function PollTasks(results) {

  

  for( var i=0; i<results.remove.length; i++ ){ 

    rowid = results.remove[i];

    // Do something with the deleted records rowid

  }

  for( var i=0; i<results.update.length; i++ ){ 

    rowid = results.update[i];

    // Do something with the updated records rowid

  }

  for( var i=0; i<results.create.length; i++ ){ 

    rowid = results.remove[i];

    // Do something with the new records rowid

  }

}

In reality, you’ll want to actually do something useful with these rowid's. For example:

  • For deleted records, remove them from your BlackBox interface

  • For new records, query each record, and add them to your BlackBox interface

  • For updated records, query each record, and update them in your BlackBox interface

Create Records

This is used for adding new records.

The create() method allows you to set an object as a  parameter. Each field you want to set a value for will be a property of that object.

You can either create the object and then pass that through as your parameter:

NewTask = new Object();

NewTask["Task::name"] = "New task name";

NewTask["Task::description"] = "New task description";

FRTB.create(NewTask).send({'onSuccess': function(){

  alert ("Record added");

}});

Or you can define the object as you’re passing it through: 

FRTB.create( 

  ["Task::name","New task name"],

  ["Task::description","New Task Description"]

).send({'onSuccess': function(){

  alert ("Record added");

}});

Or if you’ve used the bbdev_Field() and  bbdev_TO() functions to store the field name(s) and table occurrence(s) into variables, you can can use those:

FRTB.create( 

  [TO_name_var + "::" + name_field_var,"New task name"],

  [TO_name_var + "::" + description_field_var,"New Task Description"]

).send({'onSuccess': function(){

  alert ("Record added");

}});

If you have used the bbdev_relationshipKeyBuilder() function you can also set initial values based on the relationship to a table occurrence from your current context.

For example, if your BlackBox queries a found set of tasks via a relationship, and those tasks records have a type field, and the relationship includes a predicate that the type field must have a value of "booked", then this is an implied field value when creating new records. 

Otherwise, unless you explicitly set the Type  as "booked", the next time you query Task records your new record won’t be included in the found set.

To set these implied values, use the  bbdev_relationshipKeyBuilder() function to create a variable you can use to generate a list of initial creation values, and include this when invoking the .send() method for a .create() request. For example:

creation_values_var = '<?reactor bbdev_relationshipKeyBuilder ( bbdev_TO($field_name) ) reactor?>';

FRTB.create( 

  [TO_name_var + "::" + name_field_var,"New task name"],

  [TO_name_var + "::" + description_field_var,"New Task Description"]

).send({'creationValues' : creation_values_var, 'onSuccess': function(){

  alert ("Record added");

}});

Update Records

This is used for updating existing records.

The update() method allows you to set a parameter, this is an object. Each field you want to set a value for will be a property of that object.

You need to use the .where() or .filter() method, in conjunction with an update() request in order to define what records we want to update.

You can either create the object and then pass that through as your parameter:

ChangedTask = new Object();

ChangedTask["Task::description"] = "Updated task description";

FRTB.update(ChangedTask).filter("Task::name=New task name").send({'onSuccess': function(){

  alert ("Record updated");

}});

Or you can define the object as you’re passing it through: 

FRTB.update( 

  ["Task::description","Updated Task Description"]

).filter("Task::name=New task name").send({'onSuccess': function(){

  alert ("Record Updated");

}});

If your .where()  or .filter() constraint refers to multiple records, all of those records will be updated.

Delete Records

This is used for deleting existing records.

The remove() method allows you to set a parameter, this is a table occurrence of the table you wish to delete a record from.

You need to use the .where() or .filter() method, in conjunction with a remove() request in order to define what records we want to delete.

FRTB.remove("Task").where("Task.name='New task name'").send({'onSuccess': function(){

  alert ("Record(s) deleted");

}});

If you have used the bbdev_Relationship() you can use this to refer to your table occurrence. If you have used the bbdev_Field() function you can use this, as well as the .filter() method to refer to your field:

field_name_TO_var = '<?reactor bbdev_TO( $field_name ) Reactor?>';

field_name_var = '<?reactor bbdev_Field( $field_name ) reactor?>';

FRTB.remove(field_name_TO_var).filter(field_name_TO_var + "::" + field_name_var + "=New task name").send({'onSuccess': function(){

  alert ("Record(s) deleted");

}});

Calculation Request

This is used for evaluating a FileMaker calculation. 

The calc() method allows you to set a parameter, this is the calculation you wish to perform.

FRTB.calc("Get(CurrentDate)").send(function(response) {

        alert (response.result);

})

When the result of the calculation is returned via the call back function, it is accessed via the result property in the response object.

Perform Script Request

This is used for performing a script in FileMaker Pro from your BlackBox.

The script() method allows you to set two parameters, the first is the name of the script, and the second is the script parameter - which your script can access with Get(ScriptParameter). This second parameter is optional.

FRTB.script("MyScript","MyParameter").send(function(response) {

        alert("Script performed");

})

Request Configurations

When submitting any Reactor request you can include the following configurations with your send method call:

FRTB.find('dd_planets_data::name').send({

  'onsuccess' : CompletionFunction,

  'onerror' : ErrorFunction,

  'onrecordlock' : HandleRecordLock,

  'creationvalues' : creation_values_var,

  'distinct' : true,

  'databasename' : 'reactor',

  'layoutname' : 'reactor_layout'

})

  • The onsuccess, onerror and onrecordlock settings can be either names of functions, or anonymous functions

  • The creationvalues value should be a variable generated using the bbdev_relationshipKeyBuilder function

  • The distinct setting will be set to either true/false, and will set whether or not the distinct keyword is used in your queries. To the SQL-uninitiated, the query response will not include any duplicate records in its response

  • You only need to set the databasename setting if your request is intended for the non-default database - usually only relevant to FRTB.script() requests

Utility Methods

The following JavaScript methods can be used to perform less-common requests:

Set Default Database

You can use the following to set the default database to be used with other Reactor requests:

FRTB.setDefault("DatabaseName","reactor");

The above will explicitly make all Reactor requests apply to the ‘reactor’ database.

FRToolbox Version

You can use the following to return the version of FRToolbox being employed:

frt_no_var = FRTB.getVersion();

The above will store the FRToolbox version number in a variable.

Start Debugging

Output detailed debugging to the JavaScript console.

FRTB.startDebugging();

After you run this line of code any Reactor communications will be output to the console.

Date/Time Conversion

You can use the following methods to convert JavaScript dates/times to SQL date/time strings.

FRTB.getSQLDate(new Date());

FRTB.getSQLTime(new Date());

You can use the following method to convert a SQL date/time string into a Javascript date object.

FRTB.convertDateTime("2019-06-17 19:35:00");

Get Field and Table Occurrence 

You can use the following method to return the table occurrence of a fully qualified field:

FRTB.getTOName("bb_data_schedules::id",false);

If you would like to escape character spaces so you can use it in a SQL query, set the second parameter to true:

FRTB.getTOName("bb_data_schedules::id",true);

You can use the following method to return the field name of a fully qualified field:

FRTB.getFieldName("bb_data_schedules::id",false);

If you would like to escape character spaces so you can use it in a SQL query, set the second parameter to true:

FRTB.getFieldName("bb_data_schedules::id",true);

Reactor Actions

This bit is for the advanced BlackBoxers.

An action is a standardised method of giving the user a method of responding to a particular interaction.

For example, if we had a BlackBox, and we wanted to give the developer control of what happens after a FRTB.find() - but without having to delve into the JavaScript. We may want to give them the ability to specify any one of the following:

  • Run a script

  • Set a field

  • Set global field

  • Run JavaScript function

To do this, we use the FRTB.getAction method to define the action. We may want to pass through the result of our find request. For example:

var MyAction = FRTB.getAction( 'SetField=MyLayoutTO::CurrentRecord|GetField=bb_planets_data::name', 'myAction' );

FRTB.find(

  'bb_planets_data::name',

  'bb_planets_data::description',

  MyAction

).send(function(response) {

  FRTB.performAction( 'myAction', response.data[0]);

});

The first line is the construction of the action. It creates an action that sets a field called CurrentRecord on the MyLayoutTO table occurrence.

The value we set it in that field is the name value on the bb_planets_data table occurrence.

It doesn’t make sense yet? That’s because this value we’re setting is the result of a find , we’re not setting the value yet, we’re defining the field that contains the value that we will be setting.

Then we have our Reactor find request. As our parameter, we specify the fields we want returned per usual. Additionally we also include the variable name of our new action.

In our success function, we perform our action. For the first parameter we pass through the name of our action. The second parameter is the first record from our find response.

Our action knows that when we set our field we’re setting the value of bb_planets_data::name from our find response. So Reactor checks our first find response for a bb_planets_data::name value, and that’s what value is set onto MyLayoutTO::CurrentRecord.

Let’s say we don’t want to set a stored field, you want to set a global field, run a FileMaker script, or run a JavaScript function using this value (bb_planets_data::name). You simply change the parameter you set when constructing the action.

// Run FileMaker Script, passing through the field value as parameter

var MyAction = FRTB.getAction( 'Script=MyScriptName|Parameter=bb_planets_data::name', 'myAction' );

// Set Global Field, passing through the field value as our global field's value

var MyAction = FRTB.getAction( 'SetGlobal=$$MyGlobal|Parameter=bb_planets_data::name', 'myAction' );

// Run JavaScript function, passing through the field value as parameter

var MyAction = FRTB.getAction( 'Function=checkRecord();|Parameter=bb_planets_data::name', 'myAction' );

Why would we bother to do this? As you can see in the above alternatives, you could simply ask the developer to provide the actual action as a BlackBox parameter. So when a button is clicked, and you conduct the find, the developer can set the parameter to do any of the following for example:

  • Set a data/global field identifying what planet was selected

  • Run a FileMaker script that knows what planet was selected

  • Run a JavaScript function that knows what planet was selected

Obviously you could do any of these individually by altering the BlackBox code, but defining an action as a BlackBox parameter gives the user the ability to change the behaviour of your BlackBox from outside of your BlackBox.

Or you can simply deem this is as needlessly complicated and forget it forever :)

<— Reactor Home