Ok, so doing it from a form ended up a little more complicated in the sense that I pull in a few other scripts. I’m going to pull this one apart, full script at the bottom. I refactored this as I wrote this post so it might be a little inconsistent though I’ve tried to rectify it.
I’ve started in my script library leveraging a PARENT_SCRIPT variable whose existence I use to run tests in a standalone mode. To avoid the tests running, we need to define PARENT_SCRIPT at the top. The format I’ve been using has been Form Name::Script Name. This script leverages two other scripts as well: getRecordsFromFormMatchingSearch and getUniqueValuesFromField. I’ll talk about those a little later.
var PARENT_SCRIPT = 'Car Inventory::Select Vehicle Data (Form Based)';
form.runScriptNamed('getRecordsFromFormMatchingSearch');
form.runScriptNamed('getUniqueValuesFromField');
When working with different forms, it’s useful to have a complete list of all of the IDs. I ended up settling on this format that has the form name and then the field name with a suffix (formID or fldID) to make it clear what sort of object I’m dealing with. Since sometimes it’s hard to know what type a field is, that’s included in a comment at the end. This is generated by an updated version of the Form Logger script that generates Javascript variable output.
// Car Inventory
var car_inventory_formID = "frm-08d1086a93ad4cba9e452a54f93dc8bb";
var car_inventory__registration_fldID = "fld-fc1a9affa1f241359cbbaa71638e32f0"; // text
var car_inventory__make_fldID = "fld-4c02791b8dce43b2a1e83cd8d3d43201"; // text
var car_inventory__model_fldID = "fld-fb6f72e380af4cfc83b7c90383e97b80"; // text
var car_inventory__year_fldID = "fld-5a2f376a5e814902a8a42c952f881196"; // number
var car_inventory__vehicle_data_fldID = "fld-42853582b83745b491aa0d3707e4e194"; // from_form
var car_inventory__vehicle_data_watcher_fldID = "fld-72a07265e2f147e192b18df718b0a2e9"; // script
// Vehicle Data
var vehicle_data_formID = "frm-bf801eaaf00249d289e5aa4286df92a8";
var vehicle_data__year_fldID = "fld-7a2dc2f2648e4aac8c5904fe54bb0c34"; // number
var vehicle_data__make_fldID = "fld-fb04e14a4a6040a9aada1f102c6c8bfd"; // text
var vehicle_data__model_fldID = "fld-cf8d446ce3d4487d9380b59b3ab00b8f"; // text
var vehicle_data__car_inventory_fldID = "fld-bfadf961632a417b83a9acd34c4663d1"; // form
This is our callback variable that we hand to the prompter. We put this in global scope to make sure we can get at it in our callback.
var tempVar = undefined;
The promptPopup I’ve used in all three examples with slightly different Prompter parameters. This is again borrowed from something @daniel_leu wrote with some other changes. Essentially we’re creating a prompter instance with a given dialog text, popupName for the popup field and popupElements as the values. It uses a Promise to return a value from the callback:
function promptPopup(text, popupName, popupElements){
return new Promise(function(resolve, reject) {
tempVar = undefined;
let prompter = Prompter .new();
prompter.addParameter(popupName, 'tempVar', 'popup', popupElements)
prompter.cancelButtonTitle = 'Cancel';
prompter.continueButtonTitle = 'Continue';
prompter.show(text, ((status) => {
if (status == true && tempVar){
resolve(tempVar);
} else {
reject(text + " cancelled");
}
} ));
});
}
Now onto the meat. There are three prompts, the first is the make. I leverage getUniqueValuesFromField here to extract out the unique Makes from the Vehicle Data form. getUniqueValuesFromField is a helper function to make this look a little more elegant:
async function start(){
try {
let make = await promptPopup("What is the make?", "Make:",
getUniqueValuesFromField(
document.getFormNamed('Vehicle Data').getRecords(),
vehicle_data__make_fldID
)
);
console.log(make);
The next step is to display the models. Here I use a method called getRecordsFromFormMatchingSearch which is something I developed elsewhere to have a sort of key/value AND search for finding records. In this case I’m looking for all records that match that particular make. It again uses getUniqueValuesFromField to extract out the unique models for that make:
let model = await promptPopup("What is the model?", "Model:",
getUniqueValuesFromField(
getRecordsFromFormMatchingSearch(vehicle_data_formID,
[
{'key': vehicle_data__make_fldID, 'value': make}
]
), vehicle_data__model_fldID
)
);
console.log(model);
Lastly we need to find matching years. This one is similar to the last but adds model to the search parameters so that we find only years with matching makes and models.
let year = await promptPopup("What is the year?", "Year:", getUniqueValuesFromField(
getRecordsFromFormMatchingSearch(vehicle_data_formID,
[
{'key': vehicle_data__make_fldID, 'value': make},
{'key': vehicle_data__model_fldID, 'value': model }
]
), vehicle_data__year_fldID
)
);
console.log(year);
Lastly we need to catch the error and log it plus we call the function to get the ball rolling:
} catch (error) {
console.log("Error: " + error);
}
}
start();
The getUniqueValuesFromField method is a single line but it does a lot:
function getUniqueValuesFromField(recordset, fieldId)
{
return Array.from(new Set(recordset.map(function (item) { return item.getFieldValue(fieldId); })));
}
We’ll unpack it backwards:
– the inner function basically is doing getFieldValue on each record in recordset.
– map executes a function over each element of the array recordset.
– recordset is the set of records from either form.getRecords() or some other array of records.
– new Set is used to create a set of unique values.
– Array.from converts what we want to return into an array.
This makes things a little neater elsewhere and is a slight abuse of Javascript.
Here is what getRecordsFromFormMatchingSearch looks like:
/**
* Get a record from a form matching a given key/value criteria.
*
* recordset The form name or form ID to get the records from.
* criterion An array of key/value pairs of criteria to match.
*
* return Array of matched records.
*/
function getRecordsFromRecordsetMatchingCriterion(recordset, criterion)
{
// Check if our basic parameters are set.
if (!recordset || !criterion)
{
throw new Error(`Missing required parameters ${recordset}/${criterion}`);
}
//console.log(JSON.stringify(criterion));
profiler.start(`getRecordsFromRecordsetMatchingCriterion: ${recordset}/${JSON.stringify(criterion)}: start`);
let matchingRecords = new Set();
let candidateRecord = null;
for (candidateRecord of recordset)
{
let match = true;
let criteria = null;
for (criteria of criterion)
{
//console.log(JSON.stringify(criteria));
let candidateValue = candidateRecord.getFieldValue(criteria['key']);
//console.log(`Test: "${candidateValue}" vs "${criteria['value']}"`);
if (candidateValue != criteria['value'])
{
match = false;
break;
}
//console.log('Matched!');
}
if (match)
{
matchingRecords.add(candidateRecord);
}
}
profiler.end();
return Array.from(matchingRecords);
}
It’s a little more complicated, but the meat is in the middle. We start by iterating over the recordset then setting the match and criteria variables. I use let here to make sure they don’t escape into global scope inadvertently.
for (candidateRecord of recordset)
{
let match = true;
let criteria = null;
The next step is to iterate through the criterion to see if the criteria matches. These are key/value pairs where the key is the field ID and value is what we want to match against. If the candidateValue doesn’t match, we flip the match value and escape from the inner loop. I’ve left in the debugging statements that I had put in as well so you can see how that works.
for (criteria of criterion)
{
//console.log(JSON.stringify(criteria));
let candidateValue = candidateRecord.getFieldValue(criteria['key']);
//console.log(`Test: "${candidateValue}" vs "${criteria['value']}"`);
if (candidateValue != criteria['value'])
{
match = false;
break;
}
//console.log('Matched!');
}
The last step here is check if there is a match and add it to the matchingRecords.
if (match)
{
matchingRecords.add(candidateRecord);
}
}
There is one other piece of code which takes the unique set and turns it back into an array to be returned:
return Array.from(matchingRecords);
Here is the full script as one block:
var PARENT_SCRIPT = 'Car Inventory::Select Vehicle Data (Form Based)';
form.runScriptNamed('getRecordsFromRecordsetMatchingCriterion');
form.runScriptNamed('getUniqueValuesFromField');
// Car Inventory
var car_inventory_formID = "frm-08d1086a93ad4cba9e452a54f93dc8bb";
var car_inventory__registration_fldID = "fld-fc1a9affa1f241359cbbaa71638e32f0"; // text
var car_inventory__make_fldID = "fld-4c02791b8dce43b2a1e83cd8d3d43201"; // text
var car_inventory__model_fldID = "fld-fb6f72e380af4cfc83b7c90383e97b80"; // text
var car_inventory__year_fldID = "fld-5a2f376a5e814902a8a42c952f881196"; // number
var car_inventory__vehicle_data_fldID = "fld-42853582b83745b491aa0d3707e4e194"; // from_form
var car_inventory__vehicle_data_watcher_fldID = "fld-72a07265e2f147e192b18df718b0a2e9"; // script
// Vehicle Data
var vehicle_data_formID = "frm-bf801eaaf00249d289e5aa4286df92a8";
var vehicle_data__year_fldID = "fld-7a2dc2f2648e4aac8c5904fe54bb0c34"; // number
var vehicle_data__make_fldID = "fld-fb04e14a4a6040a9aada1f102c6c8bfd"; // text
var vehicle_data__model_fldID = "fld-cf8d446ce3d4487d9380b59b3ab00b8f"; // text
var vehicle_data__car_inventory_fldID = "fld-bfadf961632a417b83a9acd34c4663d1"; // form
var tempVar = undefined;
function promptPopup(text, popupName, popupElements){
return new Promise(function(resolve, reject) {
tempVar = undefined;
let prompter = Prompter .new();
prompter.addParameter(popupName, 'tempVar', 'popup', popupElements)
prompter.cancelButtonTitle = 'Cancel';
prompter.continueButtonTitle = 'Continue';
prompter.show(text, ((status) => {
if (status == true && tempVar){
resolve(tempVar);
} else {
reject(text + " cancelled");
}
} ));
});
}
async function start(){
try {
let vehicleDetails = document.getFormNamed('Vehicle Data').getRecords();
let make = await promptPopup("What is the make?", "Make:",
getUniqueValuesFromField(
vehicleDetails,
vehicle_data__make_fldID
)
);
console.log(make);
let model = await promptPopup("What is the model?", "Model:",
getUniqueValuesFromField(
getRecordsFromRecordsetMatchingCriterion(vehicleDetails,
[
{'key': vehicle_data__make_fldID, 'value': make}
]
), vehicle_data__model_fldID
)
);
console.log(model);
let year = await promptPopup("What is the year?", "Year:", getUniqueValuesFromField(
getRecordsFromRecordsetMatchingCriterion(vehicleDetails,
[
{'key': vehicle_data__make_fldID, 'value': make},
{'key': vehicle_data__model_fldID, 'value': model }
]
), vehicle_data__year_fldID
)
);
console.log(year);
} catch (error) {
console.log("Error: " + error);
}
}
start();
Attachments:
You must be
logged in to view attached files.