Tap Forms app icon half
Tap Forms Forum text image
Blue gradient background

Exchange tips and ideas with the Tap Forms community

Search Results for 'script'

Viewing 15 results - 2,311 through 2,325 (of 2,986 total)
  • Author
    Search Results
  • #37439

    Topic: Get record by key

    in forum Script Talk
    Sam Moffatt
    Participant

    I had a situation where I wanted to automatically download a set of files from the web and store them as Tap Forms document. In my case I have two forms: one parent form that stores the URL I want to grab data from as a base and second form that I want put new records with more details keyed for me by source URL.

    It takes a few parameters to operate (the last three are optional):

    • formName: The name of the source form for the records; can also be a form ID (required).
    • keyFieldId: The field ID of the key field in the record (required).
    • keyValue: The value to match as the key (required).
    • createIfMissing: Control if a record is created if none is found (default: true).
    • earlyTermination: Control if function terminates as soon as a match is found (default: true).
    • alwaysScanOnMiss: Control if we should assume concurrent access (default: false)

    I use this with my Script Manager idiom, so I have something like this:

    document.getFormNamed('Script Manager').runScriptNamed('getLinkedRecord');
    let currentRecord = getRecordFromFormWithKey('Location Tracker', source_id, entry.href, true, false);
    

    The early termination option is interesting and how I’d advise using it is dependent upon your use case. If you set early termination, when the script is iterating through all of the records it will return the first match it finds. If you only expect to call this function once, maybe twice, then this will work fine for you because depending on your ordering you will avoid a full traversal of your recordset.

    If you are running this in a loop or some other construct where you expect to call the function multiple times then disabling early termination ensures that everything is indexed on the first pass. If you expect to have many cache hits, then this will improve performance significantly as subsequent calls will immediately return based on a direct lookup. Record creation is added as a method here so that newly created records can be immediately indexed in the system avoiding future cache misses.

    The worst case for this method is a cache miss. After the first scan of the records, it assumes no other concurrent access. If you don’t want this behaviour, there is another flag to always scan if a miss isn’t found and the form has already been scanned.

    Here’s the script:

    // ========== getRecordFromFormWithKey Start ========== //
    // NAME: Get Record From From With Key
    // VERSION: 1.0
    
    /**
     * Get a record from a form with a key field, optionally creating it if missing.
     * Note: assumes your key field is unique.
     *
     * formName           The name of the source form for the records (required).
     * keyFieldId         The field ID of the key field in the record (required).
     * keyValue           The value to match as the key (required).
     * createIfMissing    Control if a record is created if none is found (default: true).
     * earlyTermination   Control if function terminates as soon as a match is found (default: true).
     * alwaysScanOnMiss   Control if we should assume concurrent access (default: false)
     */
    function getRecordFromFormWithKey(formName, keyFieldId, keyValue, createIfMissing = true, earlyTermination = true, alwaysScanOnMiss = false)
    {
    	// Check if our basic parameters are set.
    	if (!formName || !keyFieldId || !keyValue)
    	{
    		throw new Error("Missing required parameters");
    	}
    	
    	// Determine the target form (check if we were given an ID or else assume a name)
    	let targetForm = undefined;
    	if (formName.match(/frm-/))
    	{
    		targetForm = document.getFormWithId(formName);
    	}
    	else
    	{
    		targetForm = document.getFormNamed(formName);
    	}
    
    	// Check if our global variable has been setup.
    	if (!indexedRecordIndex)
    	{
    		var indexedRecordIndex = {};	
    	}
    	
    	// Flag for if we've indexed this form already.
    	if (!indexedRecordState)
    	{
    		var indexedRecordState = {};
    	}
    
    	// Create a key for this form-field combination.
    	// Form+Field is the key.
    	let indexedRecordKey = targetForm.getId() + "_" + keyFieldId;
    
    	// Check to see if this particular link field has been setup.
    	if (!indexedRecordIndex[indexedRecordKey])
    	{
    		indexedRecordIndex[indexedRecordKey] = {};
    	}
    
    	
    	// Short circuit if we have an exact match.
    	if (indexedRecordIndex[indexedRecordKey][keyValue])
    	{
    		return indexedRecordIndex[indexedRecordKey][keyValue];
    	}
    	
    	// No immediate match, check to see if we should scan everything.
    	// alwaysScanOnMiss forces this code path each execution.
    	// The check to indexedRecordState is if this has been indexed.
    	if (alwaysScanOnMiss || !indexedRecordState[indexedRecordKey])
    	{	
    		// Brute force search :(
    		let records = targetForm.getRecords();
    
    		// Iterate through all of the records and look for a match.
    		for (currentRecord of records)
    		{
    			// Set up a reverse link for this value.
    			let recordKeyValue = currentRecord.getFieldValue(keyFieldId);
    			indexedRecordIndex[indexedRecordKey][recordKeyValue] = currentRecord;
    
    			// If this is a match and early termination is setup, return immediately.
    			if (earlyTermination && recordKeyValue == keyValue)
    			{
    				return currentRecord;
    			}
    		}
    		
    		// Flag this record-field as being indexed.
    		indexedRecordState[indexedRecordKey] = true;
    	}
    
    	// Check to see if we got a match here and return if the key exists.
    	if (indexedRecordIndex[indexedRecordKey][keyValue])
    	{
    		return indexedRecordIndex[indexedRecordKey][keyValue];
    	}
    	else if (createIfMissing)
    	{
    		// If createIfMissing is set, create a new record.
    		// Note: it's expected the caller will call document.saveAllChanges();
    		let newRecord = targetForm.addNewRecord();
    		// Set the key value to our search value.
    		newRecord.setFieldValue(keyFieldId, keyValue);
    		// Link this up to save us another lookup in future.
    		indexedRecordIndex[indexedRecordKey][keyValue] = newRecord;
    		// And now we return the new record. 
    		return newRecord;
    	}
    	
    	// If we didn't find anything, return undefined.
    	return undefined;
    }
    // ========== getRecordFromFormWithKey End ========== //
    
    #37397
    Sam Moffatt
    Participant

    I created a form called ‘Script Manager’ which I use to put form scripts and then you can load them like this:

    document.getFormNamed('Script Manager').runScriptNamed('Form Logger');
    

    I use this idiom for a bunch of sample scripts you can find on the forum.

    #37395
    Rocky Machado
    Participant

    Hi – Not sure if I am using the right terminology, but How do I create global functions?
    for example, I my have a formatting function that I want to use in different Scripts that I create within a form.

    Thanks,
    rocky

    #37390
    Brendan
    Keymaster

    Hi Mackay,

    Yes, you can do this in a Tap Forms formula. But it’s much easier to deal with in a Script field rather than a Calculation field.

    Your example though doesn’t quite make sense because there’s no condition in the first parameter.

    You need to compare Field with something.

    For example:

    IF(Field = 10; "Spring"; IF(Field = 20; "Summer"; IF(Field = 30; "Fall"; "")))

    So if field is 10, return Spring, otherwise if field is 20, return Summer, otherwise if field is 30, return Fall, otherwise return the empty string.

    But in a Script, it would look like this:

    if (field == 10) {
       return "Spring";
    } else if (field == 20) {
       return "Summer";
    } else if (field == 30) {
       return "Fall";
    } else {
       return ""
    }

    To me that’s just easier to read than the nested IF() function.

    You can also use a switch statement, which might look nicer.

    Thanks,

    Brendan

    #37388

    In reply to: External Apps

    Brendan
    Keymaster

    Hi Denny,

    The Default Value option is your only way of launching another app from within Tap Forms. You can put parameters in the URL that get their values from other fields if you might need that kind of functionality. Just put the field name within the URL like this: https://www.tapforms.com/%5Bsome other field] and Tap Forms will substitute in the value for you.

    You can use a script to fetch an image from a URL and populate a Photo field with it in Tap Forms.

    See the Scripts section of the user manual for details:

    https://www.tapforms.com/help-mac/5.3/en/topic/scripts

    There’s even a Fetch Movie Data from Web Service example that shows how to fetch data from a web service, including getting a photo from a URL:

    record.addPhotoFromUrlToField(imageUrl, case_cover);

    Hope that helps!

    Brendan

    #37371
    Rocky Machado
    Participant

    Hi – I actually have two questions about Recalculating. I currently have an import form that I used to review transactions (data scrubbing) before I put them into the final form.

    My First question, I have a Purge All Transactions function that cycles through all the records and deletes them so I can begin a new import (usually 10-20 transactions a day). I notice when I do this it appears that the records are not deleted, but when I click the refresh record list button then the go away. Is there a method that would allow me to refresh the list? or will I always need to click on the refresh record list button to refresh the form?

    My Second Question, In my import form I have several scripts that I created to parse certain fields in the import process. I noticed when I import my records I have to click on the refresh record list form for my scripts take affect. Is there a method or process to force a recall or do I also need to click on the refresh record list button to refresh my records?

    thanks,
    rocky

    #37363
    Daniel Leu
    Participant

    I’m not certain that your comparison works. What is the content of fld-cf720b6ab4314f0bb5f47bc9bd61f0a9 supposed to be? The string ‘search’? Then it should be in quotes.

    Sidenode: I recommend to define all the fields used in a script at the beginning of a script or at the beginning of a function. And then use the identifiers later on. This increases readability, specially when you revisit a script that you wrote a while ago.

    var field_a_id = 'fld-....';
    var field_b_id = 'ld-....';
    ...
       if (rec.getFieldValue(field_a_id) == search) {
          // if match, do something
    	Utils.copyTextToClipboard(rec.getFieldValue(field_b_id));
          break;
       }
    ...
    

    Cheers, Daniel

    ---
    See https://lab.danielleu.com/tapformspro/ for scripts and tips&tricks

    #37355
    David Gold
    Participant

    That’s strange. It started coming up for me as an app once I added my first Siri Shortcut. I see it in Shortcuts in the app list and when I click on it I see the scripts I have set up via Siri.

    #37345
    David Gold
    Participant

    On iOS if I add a Siri to a script and then create a Shortcut which runs that script as an action step is there any way to pass data either into the script or retrieve from the script? I can’t see anything on this in the API unless I’m missing it. If you can’t do this directly is there a workaround via the clipboard (ie can a script get information from the clipboard or can it at completion do that or display a result without having to launch Tap Forms)?

    #37344

    In reply to: record or records ?

    Sam Moffatt
    Participant

    Do you mind sharing a sample form demonstrating the problem? I’m not sure I quite follow.

    One though could be changing from var to let. var puts a variable in global scope whilst let isolates it to block scope. What is block scope? Any code at the same or lower level of brace. The Mozilla JavaScript reference on let has some details about use cases.

    There is also another caveat that sometimes Tap Forms will reuse a Javascript context.

    #37336
    Marcus
    Participant

    Hi,
    I am trying to figure out to limit a script execution for a list of records or for a currently open recordset.

    Behavior of the script below:
    When I open a table it works as expected.
    By opening a recordset of this table it works as expected as well.
    But when I go back to the list of recordsets
    the scripts assumes a recordset is still open.

    My script may be wrong, so my main question is how to detect if there‘s a list (recordsets) or a single recordset opened.

    Use case:
    I do have scripts accessing ALL recordsets
    and I do have scripts accessing only fields of the current opened recordset.
    I‘d like to prevent the execution in the wrong view.

    function check() {
       try {
          var res=record.getFieldValue('fld-76b2c64fccc34c20b62b7d485f5bfa28');
       } catch (err) {
          return 2;
       }
    }
    
    function myFunction() {
       Utils.alertWithMessage('Hello', 'Here I am !');
    }
    
    var tmp=check();
    if (tmp != 2){
       myFunction();
    } else {
       Utils.alertWithMessage('Notice', 'A recordset must be opened');
    }
    #37293
    David Gold
    Participant

    I’m not great with JavaScript but know some basics. I’m trying to write a script to search for a certain record (by searching a specific field) and when found return the contents of a separate specific field. Is anyone able to help?

    #37289
    Sam Moffatt
    Participant

    Ok so essentially you’re wanting to treat this like a bank account where each record is a line item that has a transaction value and then updates the balance from a starting point.

    Since you want to do per record storing that’s a little more nuanced, I’d actually go with a form script. I’m assuming you’re sorting records by something like date and what ever you’re using needs to be consistent. I’m going to assume this is ordered in a way that makes sense.

    Something like this should do the trick:

    function Calc_Balance() {
    	let balance = 3000000;
    	var amount_id = 'fld-454d9fcbfabd4e3fb89823668e042079';
    	var balance_id = 'fld-69d8d05d722f4dda9691dd687ed3b96c';
    	var transaction_date_id = 'fld-0ce56448807849699dfb2a22bb984774';
    	
    	for(record of form.getSearchNamed('Transaction Date: 1/10/19').getRecords())
    	{
    		balance -= record.getFieldValue(amount_id);
    		record.setFieldValue(balance_id, balance);
    	}
    	document.saveAllChanges();
    }
    
    Calc_Balance();
    

    The script is a little brute force which means it’ll rewrite everything but seems to work.

    Attaching a form template that has this script and an autogenerate script in it to generate sample data quickly.

    Attachments:
    You must be logged in to view attached files.
    #37280
    john cesta
    Participant

    OK I’m doing my best. I use your code Sam and it resolves but I don’t get a result anywhere.

    Here’s what I’m trying to do….
    I can win an apple watch if I write more than 3,000,000 of insurance volume before the end of the year,

    So I search for the policies I wrote between October and Dec 31 And have a total of volume.

    Now I want to, in each record, count down from the 3,000,000 to see where I’m at.

    So I reckon I need a form search to gather all the records that count and a record script to store the total of the search records in order to use the total to subtract from the 3,000,000

    #37277
    john cesta
    Participant

    In other words I want to run this script which totals the volume of only the selected records as in the image. But totals all the records in the form.

    function Volumecontest() {

    // Replace with your own code

    var volume_id = ‘fld-9b16cf5c9ee4420c83274f23ba59e554’;
    var total = form.getTotalOfField(volume_id);

    return total;

    }

    Volumecontest();

    Attachments:
    You must be logged in to view attached files.
Viewing 15 results - 2,311 through 2,325 (of 2,986 total)