Extending Survey123 smart forms with custom JS functions

60332
93
08-06-2020 06:22 PM
IsmaelChivite
Esri Notable Contributor
10 93 60.3K

[Last Update: July 6, 2023]

 

Custom JavaScript functions complement XLSForm expression syntax, giving you flexibility to build more complex calculations, data validation rules and constraints.

This blog provides guidance to get you started with custom JavaScript functions. For completeness, check the pulldata JavaScript help topic. It assumes familiarity with Survey123 Connect, XLSForm syntax and JavaScript.

 

Getting started

 

Let's start with a simple scenario. In Survey123 Connect, create a new survey and add a couple of questions as shown below. Our goal is to create a custom JavaScript function to calculate the greeting question.

IsmaelChivite_1-1688863048745.png

Using regular XLSForm syntax, we could calculate the greeting as follows:

concat("Hello ", ${myname})

This already teaches us something: custom JavaScript functions are not always the best approach. If you can solve something easily using pure XLSForm functions, do not use a custom JavaScript function. In our case, we will use this example only because it helps us focus on the basics of setting up a custom JavaScript function.

To invoke a JavaScript function from XLSForm, we use the puldata() function. For example:

pulldata("@javascript","myFunctions.js","HelloMe",${myname})

The parameters for the pulldata() function are as follows. First we pass "@javascript" to indicate that we want to execute a JS function. Then, we pass the name of the JavaScript file that contains our function, which in this example is "myFunctions.js". The next parameter is the name of the function within the file that we want to call: "HelloMe". Lastly, we pass as many parameters as the JS function takes. In our case, we will just pass the name of the survey taker, which is contained in the ${myname} question. If the JS function takes more parameters, we would add them in our pulldata() function call as more parameters separated by commas.

IsmaelChivite_3-1688863301021.png

For the pulldata() function above to work, we need to create a "myFunctions.js" file with its corresponding "HelloMe" JavaScript function. In fact, as you refresh your survey preview in Connect you will get a File not found: myFunctions.js error. That is totally expected.

Custom JavaScript files are stored in the survey directory, within a folder called scripts. In the old days, you had to create the folder manually and add your JavaScript files to it. Starting with version 3.12, you will see a Scripts tab at the bottom of Survey123 Connect that will help you with the process as shown in the next animation:

2023-07-10 Custom JS Survey123.gif

 

Next, you can add your own JavaScript function (or functions) to the file. For example:

function HelloMe(whosthere) {

    return "Hello, " + whosthere;
}

Do not forget to click the Save button on the bar in the right side!

You can test and even copy the pulldata() function to invoke your function right from the Scripts tab.

 

2023-07-10 Custom JS Survey123 v2.gif

 

Once the file is saved in the scripts folder, make sure your pulldata() function in the XLSForm is invoking the JavaScript function correctly, and give it a try.

Naturally, you will find a few bumps before you get your JavaScript functions working. Here are some of the most common errors that you will encounter:

File not found: myFunctions.jsYour pulldata() function is trying to load a JavaScript file that cannot be found in the scripts folder
Error in myFunctions.js : 6:16 Expected token `;'Syntax error in line 6 of your function.
@javascript error:TypeError: Property 'HelloMe' of object [object Object] is not a function in myFunctions.js:HelloMeYour pulldata() function is trying to invoke a function that cannot be found in the JS file you specified.

 

When writing your own custom JavaScript functions for execution within your Survey123 form, remember that your code will not run within the context of a web browser; you are limited to JavaScript ES6. You can't use DOM objects, or frameworks like JQuery, Ember, Angular etc. You can't access local files or make asynchronous calls either. Despite all these limitations, there is still quite a bit you can do!

 

Once you have your JavaScript function working, you can publish your survey. Custom JS functions are supported in online surveys as well as in the Survey123 field app. However, keep in mind that JS functions will not execute unless a user is signed in to the Survey123 field app or web app.

 

Parsing complex data structures

 

A common use for custom JavaScript functions is to parse complex structures, so you can extract key information from them to calculate questions in your form. From an XLSForm perspective, the syntax in your Survey123 form is really not much different from what you already learned in the Getting started section. The real complexity is handled inside the JavaScript function itself.

As an example, let's take the contents of an AAMVA PDF417 barcode. This type of barcode is used in driver licenses and encodes information such as name, birthday and many other things. Since the Survey123 field app has built-in barcode capabilities, you can scan such a barcode. The contents would look something like this:

AAMVA

This JavaScript function formats the AAMVA string from a driver's license into a JSON object, which can then easily be used within XLSForm to extract the specific information you are looking for:

 

function DL2JSON (data) {
    var m = data.match(/^@\n\u001e\r(A....)(\d{6})(\d{2})(\d{2})(\d{2})/);
    if (!m) {
        return null;
    }

 

    var obj = {
        header: {
            IIN: m[2],
            AAMVAVersion: parseInt(m[3]),
            jurisdictionVersion: parseInt(m[4]),
            numberOfEntries: parseInt(m[5])
        }
    };

 

    for (var i = 0; i < obj.header.numberOfEntries; i++) {
        var offset = 21 + i * 10;
        m = data.substring(offset, offset + 10).match(/(.{2})(\d{4})(\d{4})/);
        var subfileType = m[1];
        var offset = parseInt(m[2]);
        var length = parseInt(m[3]);
        if (i === 0) {
          obj.files = [ subfileType ];
        } else {
          obj.files.push(subfileType);
        }
        obj[subfileType] = data.substring(offset + 2, offset + length - 1).split("\n").reduce(function (p, c) {
            p[c.substring(0,3)] = c.substring(3);
            return p;
        }, { } );
    }

 

    if (obj.DL) {
        ["DBA", "DBB", "DBD", "DDB", "DDC", "DDH", "DDI", "DDJ"].forEach(function (k) {
            if (!obj.DL) return;
            m = obj.DL.match(/(\d{2})(\d{2})(\d{4})/);
            if (!m) return;
            obj.DL = (new Date(m[3] + "-" + m[1] + "-" + m[2])).getTime();
        } );
    }

 

    return JSON.stringify(obj);
}

This is what the actual XLSForm would look like. Note that the myjson question uses pulldata("@javascript") to first convert the output from the barcode question into a JSON string. Then the pulldata("@json") function is used to extract specific attributes from the string.

IsmaelChivite_5-1688864012394.png

Tip: Set the value of bind::esri:fieldType to null in the myjson question if you do not want to store the aamva raw string in your feature layer, but still be able to process it within your form logic.

Working with repeats

 

Custom JavaScript functions are ideal for processing data in repeats. As of version 3.18, using repeats with custom JavaScript functions is limited to the Survey123 field app. You can retrieve all values for a question within a repeat, or retrieve all records within a repeat.

 

Passing a question within a repeat to pulldata("@javascript")

 

When you pass a question within a repeat to pulldata("@javascript"), the JavaScript function receives an array of values for the specified question. For example, lets say we want to display a warning message if the user has introduced duplicate values in a question within a repeat. In this case, we want to create a JavaScript function that takes an array and returns true if duplicate values are found. Something like this:

function HasDups (myArray)
{
 return new Set(myArray).size !== myArray.length;
}

Now all we need to do is to call the function and pass the question within the repeat to it:

IsmaelChivite_7-1688865800185.png

 

When passing a question within a repeat to pulldata("@javascript"), it is important to keep the pulldata("@javascript") outside the repeat. In the example above, note that I first keep the result of the duplicates check outside the repeat, and then I use that value in the constraint expression for the fruit question.

 

Passing a repeat to pulldata("@javascript")

 

You can also pass an entire repeat to pulldata("@javascript"). In this case, your JavaScript function will receive all records within the repeat as an array. Each item in the array is in turn another array representing the values for that record.

In our fruits example above, let's pretend we want to calculate how many bananas have been entered. If we pass the entire fruits repeat, we can use a JavaScript function to loop through every record. If the fruit in the record is banana, then we get the quantity value and add it to our total.

function TotalBananas (fruitsrepeat)
{
 var totalBananas = 0;
 var i;
 for (i = 0; i < fruitsrepeat.length; i++) {
  if (fruitsrepeat[i].fruit=='banana') {
     totalBananas = totalBananas + fruitsrepeat[i].quantity;
  } }
 return totalBananas;
}

Here is the XLSForm:

IsmaelChivite_8-1688866356162.png

 

Working with web services

 

Using a custom JavaScript function, you can invoke a web service. Here is an example that takes a VIN number and returns information for that vehicle.

 

// Query the NHTSA to return information about a vehicle based on it's VIN

// Refer to https://vpic.nhtsa.dot.gov/api/ for more information

function decodeVIN (VIN){

  // Output value. Initially set to an empty string (XLSForm null)

  let outValue = "";

  // Check the length to make sure a full VIN is provided

  if (VIN.length<11){

    return outValue;

  }

  // Add the VIN to the decode VIN API request

  let url = `https://vpic.nhtsa.dot.gov/api/vehicles/decodevinvalues/${VIN}?format=json`;

  // Create the request object

  let xhr = new XMLHttpRequest();

  // Make the request. Note the 3rd parameter, which makes this a synchronous request

  xhr.open("GET", url, false);

  xhr.send();

  if (xhr.status !== 200) {

    } else {

    outValue = xhr.responseText;

  }

return outValue;

}

More Samples

 

Survey123 Connect includes a new XLSForm sample illustrating how to use custom JavaScript functions in multiple scenarios. As you get hands-on, this is a sample worth checking.

 

IsmaelChivite_9-1688867009779.png

 

Limitations

 

  • Document Object Model (DOM) is not supported.
  • Frameworks such as JQuery, Ember, and Angular are not supported.
  • You cannot access local files.
  • Asynchronous calls are not supported.
  • JavaScript functions are only supported in forms completed by users in the same organization as the form author.
  • JavaScript functions are not supported for public surveys.
  • A pulldata("@javascript") function cannot be called inside a pulldata("@json") function on the Survey123 web app.
  • Passing repeats or questions within a repeat to a JS function only works in Connect and the mobile app
93 Comments
ATTEC_SLUConsultores
New Contributor II

Muchas gracias.

Ya me estaba rompiendo yo mucho la cabeza con este tema. La verdad es que me limita bastante, porque quiero compartir el formulario con histórico con miembros de un grupo de otras organizaciones, pero si no es posible, tendré que buscar otras alternativas.

Gracias por compartir.

ChristalHigdon_USFS
New Contributor III

@IsmaelChivite - I am trying to pull data from a csv file for inspections where the csv file data has a 1:M relationship to the id of the survey item. So for the current bridge being inspected, the related csv file contains multiple records related by that bridge ID. We need to pull that data into the survey for the inspector to use. I have seen this question for other inspection types from others trying to do the same thing. How do we reference the csv file in the survey's media folder in our javascript for using pulldata? Is this even possible? If so, another example in the Survey123 JavaScript sample showing the basics of how to do this would be incredibly helpful.

Thank you,
Christal Higdon
US Forest Service

MathieuBoonen
Occasional Contributor

When I use the JS Function I get this string back as per below, but because the format is not true JavaScript I cannot seem to extract the one part of the record I want. which is the actual measurement  and date as two seperate strings (2021-11-15T07:33:00 and 7.6 below )from a rain gauge, so quite dependent on date time and location and as such is quite dynamic to the point when no data is available it has no value for the rain amount.

<?xml version="1.0" ?>

<Hilltop>

<Agency>Gisborne District Council</Agency>

<Measurement SiteName="Waerenga-O-Kuri">

<DataSource Name="Rainfall" NumItems="1">

<TSType>StdSeries</TSType>

<DataType>SimpleTimeSeries</DataType>

<Interpolation>Histogram</Interpolation>

<ItemInfo ItemNumber="1">

<ItemName>Rainfall</ItemName><ItemFormat>F</ItemFormat><Units>mm</Units><Format>#.###</Format>

</ItemInfo>

</DataSource>

<Data DateFormat="Calendar" NumItems="1">

<E><T>2021-11-15T07:33:00</T><I1>7.6</I1></E>

</Data>

</Measurement>

</Hilltop>

<?xml version="1.0" ?>

<Hilltop>

<Agency>Gisborne District Council</Agency>

<Measurement SiteName="Waerenga-O-Kuri">

<DataSource Name="Rainfall" NumItems="1">

<TSType>StdSeries</TSType>

<DataType>SimpleTimeSeries</DataType>

<Interpolation>Histogram</Interpolation>

<ItemInfo ItemNumber="1">

<ItemName>Rainfall</ItemName><ItemFormat>F</ItemFormat><Units>mm</Units><Format>#.###</Format>

</ItemInfo>

</DataSource>

<Data DateFormat="Calendar" NumItems="1">

<E><T>2021-11-15T07:33:00</T><I1>7.6</I1></E>

</Data>

</Measurement>

</Hilltop>

How would I get these two parameters from the returned result.

Juan_LuisGarcía_González
New Contributor

Buenas tardes, me gustaría saber si existe un código de JavaScript que permita tomar el dato de una tabla que se encuentre en ArcGIS online, y que ese dato se muestre en un encuesta, como el valor inicial de una casilla, el dato seria de tipo numérico (kilómetros).

 

gracias de antemano

HelenZhou
Occasional Contributor II

@IsmaelChivite Is there a way to populate a dropdown of a field in the survey by pulling a field data in a map/feature service or a linked csv file? Thanks

KimberlyMcCallum
New Contributor III

Hi @HelenZhou! I'm not sure how to do this with JavaScript but that would be very interesting... Another approach would be to use the search() in the appearance field as described here: https://community.esri.com/t5/arcgis-survey123-blog/dynamic-choice-lists-using-search-appearance/ba-...

You can reference a feature service or table and can add filter parameters... it's quite slick but be warned, that it seems to have some problems when utilized within repeats.

 

HelenZhou
Occasional Contributor II

I am using the javascript function to calculate some values from a publicly accessible arcgis feature service. It looks like, the javascript function only works when logged in with an arcgis online account. For public anonymous survey, it doesn't work. Doe anybody have the same experience?

anonymous55
Occasional Contributor II

Hello,

In first  survey I have ID number question which user type.
In 2nd survey I want user to type ID and bring all information from first Survey which they type ID on that. I know I can do this by JaveScript and pulldata but I don't know JS. basically,  I need users put ID number and then all info from first survey show on 2nd survey.

I appreciate if I have step by step instruction with JS code.

Thanks

JerrySneary
New Contributor III

Hi @IsmaelChivite,

I am trying to accomplish the same thing as @AdministradorData_Gis. I have my form set up where the person chooses their "department" and then their "name." I'm using the search() function to pull data from a hosted feature layer. After they select their name I would like to use the pulldata() function to fill in their phone number and email. It's for the very same reason as @AdministradorData_Gis I set up a hosted feature layer so that each department can keep up with new employees and employees that have left. Are you able to use JS Functions to accomplish this? Has ENH-000119959 been resolved, if that applies to what I want to accomplish? 

JerrySneary_0-1642026354029.png

 

HelenZhou
Occasional Contributor II

Hi @JerrySneary I am able to use Javascript to search for other information in a hosted service. As I have posted question above, it works. But the users who take the survey have to have an ArcGIS online account to use the Javascript function to populate the lookup information. I am not sure if that is by design.

anonymous55
Occasional Contributor II

Hey @HelenZhou
Could you send your steps specially JS code and how to link to another hosted service?
I couldn't find any code sample.

 

Jesan90
New Contributor II

Hi @JerrySneary 

I have done what you comment through the following flow:

For example to search for customer information


1. With the search () function, I choose the customer of a service hosted on AOL.
2. With the previous selection, I make a calculation through a javascript script, in which, I consult the service with the customer 's data, filtering by the selected customer, as a result of the script I obtain a json with the information.
3. Finally, I do a calculation with the pulldata () function, to extract what I want from the json.

I leave you an image and the code of the script

Jesan90_0-1642094909524.png

Replace <> with your data

function QueryInfoClientes(ID,Token){

    var xmlhttp = new XMLHttpRequest();
    var token = Token  // This is a field token calculation
    var url = "<YOUR SERVICE>" // For example: https://services.arcgis.com/xxxxxxx/ArcGIS/rest/services/xxxxx/FeatureServer/x/
					+"query?where=<YOUR FILTER FIELD>%3D%27"
					+ ID // The parameter filter
					+ "%27&objectIds=&time=&resultType=none&outFields=*&returnHiddenFields=false&returnIdsOnly=false&returnUniqueIdsOnly=false&returnCountOnly=false&returnDistinctValues=false&cacheHint=false&orderByFields=&groupByFieldsForStatistics=&outStatistics=&having=&resultOffset=&resultRecordCount=&sqlFormat=none&f=pjson"
				    + "&token=" + token 
			
    xmlhttp.open("GET",url,false);
        xmlhttp.send();

    if (xmlhttp.status!=200){
        return "Error1"
    } else {
        var outValue = xmlhttp.responseText;	
        }
	return outValue;
}

 

 

anonymous55
Occasional Contributor II

Hello @Jesan90 

I have this code but I am getting error of Token.
Basically, I want user type scooter ID and get some information from survey for example vehicle_company or etc.

function returnFeatures2(ID,Token){

    var xmlhttp = new XMLHttpRequest();
    var token = Token  // This is a field token calculation
    var url = "https://services.arcgis.com/****/ArcGIS/rest/services/-*****/FeatureServer/0"
					+"query?where=vehicle_id"
					+ ID // The parameter filter
					+ "%27&objectIds=&time=&resultType=none&outFields=*&returnHiddenFields=false&returnIdsOnly=false&returnUniqueIdsOnly=false&returnCountOnly=false&returnDistinctValues=false&cacheHint=false&orderByFields=&groupByFieldsForStatistics=&outStatistics=&having=&resultOffset=&resultRecordCount=&sqlFormat=none&f=pjson"
				    + "&token=" + token 
			
    xmlhttp.open("GET",url,false);
        xmlhttp.send();

    if (xmlhttp.status!=200){
        return "Error1"
    } else {
        var outValue = xmlhttp.responseText;	
        }
	return outValue;
}

 

ARM_0-1642192311838.png

 

Ming
by
Occasional Contributor

@anonymous55 I think you only passed s_id to JavaScript and DIDN'T pass GetToken.

anonymous55
Occasional Contributor II

@Ming 
Man This is so confusing I don't know js 🙂 

Ming
by
Occasional Contributor

You once did correct here 🙂 @anonymous55 

Ming_0-1642196303202.png

 

anonymous55
Occasional Contributor II

I am thinking my code is not correct I will test it 
Thanks a lot

Jesan90
New Contributor II

Hi @anonymous55 

try these fixes 😉

Jesan90_0-1642206787340.png

 

Jeff,

Jesan90
New Contributor II

Hi @anonymous55 

I forgot it, try this too 

Jesan90_1-1642207415920.png

 

anonymous55
Occasional Contributor II

Hello all

I have these so have and I am getting this error:

ARM_0-1642524967153.png


JS Code

 

function returnFeatures2(ID,Token){

    var xmlhttp = new XMLHttpRequest();
    var token = Token  // This is a field token calculation
    var url = "https://services.arcgis.com/*****/ArcGIS/rest/services/******/FeatureServer/0"
					+"query?where=vehicle_id%3D%27"
					+ ID // The parameter filter
					+ "%27&objectIds=&time=&resultType=none&outFields=*&returnHiddenFields=false&returnIdsOnly=false&returnUniqueIdsOnly=false&returnCountOnly=false&returnDistinctValues=false&cacheHint=false&orderByFields=&groupByFieldsForStatistics=&outStatistics=&having=&resultOffset=&resultRecordCount=&sqlFormat=none&f=pjson"
				    + "&token=" + token 
			
    xmlhttp.open("GET",url,false);
        xmlhttp.send();

    if (xmlhttp.status!=200){
        return "Error1"
    } else {
        var outValue = xmlhttp.responseText;	
        }
	return outValue;
}

 


Xlsx Form

ARM_1-1642525156508.png

 

Jesan90
New Contributor II

Hi @anonymous55 

Try this:

Jesan90_0-1642526683345.png

 

Jeffry J,

 

LMedeirosUI
Occasional Contributor

@SeanRedar You may have already figured this out by now, but I don't think custom JS functions work in the webform. 

Not true (maybe never was, but definitely isn't now). However, users must be signed in for custom JS functions to work.

PJ
by
New Contributor

Hi @IsmaelChivite,

I am using pulldata function (pulldata("@json", ${jsonresponse}, "weather[0].main") ) to pull weather data from https://home.openweathermap.org/.

I plugged the weatherkey code (32 characters) into the default field in the Survey123 form to pull weather data. Its working fine in survey123 app but its not working on Survey123 website. I changed the survey from Public survey to private, still no luck. 

I will appreciate if you could help me.

Here are the screen shots 

PJ_0-1649172551941.png 

PJ_1-1649172622954.pngPJ_2-1649172690126.png

 

Thanks

 

HeathAnderson
Occasional Contributor II

Hi @JamesTedrick,

I saw that you have be post some messages on this blog regarding PDF417 and the barcode scanner.  I can seem to validate a match using the code in above.  When I test again some sample data or even real data I can't get past the data.match regex expression.  It only is returning "null".

It looks like it must be something with the regex.  Any thoughts?  I have attached my sample PDF417 I have been working with.  

 

@

ANSI 636037040002DL00410514ZI05550117DLDCAX-1X-2
DCBX-1X-2X-3X-4
DCDX-1XY
DBA07042010
DCSSAMPLEFAMILYNAMEUPTO40CHARACTERSXYWXYWXY
DACHEIDIFIRSTNAMEUPTO40CHARACTERSXYWXYWXYWX
DADMIDDLENAMEUPTO40CHARACTERSXYWXYWXYWXYWXY
DBD07042006
DBB07041989
DBC2
DAYHAZ
DAU5'-04"
DAG123 SAMPLE DRIVE                   
DAHAPT B                              
DAIINDIANAPOLIS        
DAJIN
DAK462040000  
DAQ1234-56-7890             
DCF07040602300001           
DCGUSA
DDEN
DDFN
DDGN
DAZBLN         
DCK12345678900000000000     
DCUXYWXY
DAW120
DDAF
DDBMMDDCCYY
DDD1

ZIZIAMEDICAL CONDITION
ZIBMEDICAL ALERT
ZIC023
ZIDDONOR
ZIEUNDER 18 UNTIL 07/04/07
ZIFUNDER 21 UNTIL 07/04/10
ZIGOP

 

Thank you

Best,

Heath

EvanR
by
New Contributor III

@IsmaelChivite, I am trying to do exactly what you describe in on of the examples above (retrieve a value from the last instance of a repeat) and have copied your script , but I am getting the error "@javascript error:ReferenceError: conditionsArray is not defined in myFunctions.js". I have tried it in my survey and also in the JS sample survey, both with the same result. The script shows up correctly in Survey123 Connect. I have tried everything I can think of, and always with the same result. I would appreciate any suggestions. Am I missing a piece of the script defining conditionsArray that is not shown in the example?

Thanks,
Evan Raskin
National Park Service

UPDATE: I don't know if this represents an error in the example script, but changing "conditionsArray" to "questionInRepeat" fixed the error. Thanks to Sarah Wright for the suggestion. Unfortunately it does not work across nested repeats.

bsklaohfl
New Contributor III

Hi,


I realize this is an old post but curious if someone could help me with the Javascript for the AAMVA PDF 417 for Florida. I think I've exhausted all "help" resources.

My barcode is returned as follows (private info removed):

@ ‑ ANSI 636010090002DL00410264ZF03050075DLDAQS123456789123 DCSLASTNAME DDEN DACFIRSTNAME DDFN DADMIDDLENAME DDGN DCAE DCBNONE DCDNONE DBDDATEOFDL DBBDOB DBAEXPIRATION DBC2 DAU067 IN DAGADDRESS DAICITY DAJFL DAKZIP DCFF1234 DCGUSA DCK1234 DDAF DDB1234 DDK1 ZFZFA20220906 ZFB ZFCSAFE DRIVER ZFD ZFE ZFF ZFG ZFH ZFI ZFJ0246553392 ZFK

 

I figured I needed to modify the function DL2JSON. My modification is as follows:

function DL2JSON (data) {

var m = data.match(/^@\-\(A....)(\d{6})(\d{2})(\d{2})(\d{2})/);

if (!m) {

return null;

}

 

The error I am receiving is:

@javascript error:TypeError: Property 'match' of object [object Object] is not a function in aamva.js:DL2JSON

 

Any insight would be seriously appreciated. Thank you in advance,

Brooke

RViruet
New Contributor III

Hey @bsklaohfl,

Right now I can't test the Barcode functionality for S123, but I did notice you're missing a "}" in the code snippet you posted.

Here's the corrected code:

function DL2JSON (data) {
	var m = data.match(/^@\-\(A....)(\d{6})(\d{2})(\d{2})(\d{2})/);

	if (!m) {
	return null;
	}
}



I Did test the calculation expression passing some random string and it didn't raise any errors.

bsklaohfl
New Contributor III

Hi @RViruet ,

 

Thank you for your response! I went ahead and changed that but am unfortunately still getting the error: "@javascript error:TypeError: Property 'match' of object [object Object] is not a function in aamva.js:DL2JSON".

LMedeirosUI
Occasional Contributor

@bsklaohfl This looks like the error message you would get if the pulldata() function in your XLS form is invoking "match" as a function to call (within aamva.js:DL2JSON). What does your XLS form look like (in terms of the JS questions)?

Is there anything in your javascript function that is out of line? Could you post it in its entirety?

bsklaohfl
New Contributor III

@LMedeirosUI, thank you for your reply. My XLS form is attached, as well as my Javascript function. I have just copied and pasted from the example above.

 

2023-03-22 15_53_26-bsklaohfl_xls_Form7.xlsx - Excel.png

 

		function DL2JSON (data) {
        var m = data.match(/^@\-\(A....)(\d{6})(\d{2})(\d{2})(\d{2})/);
        if (!m) {
            return null;
        }

     

        var obj = {
            header: {
                IIN: m[2],
                AAMVAVersion: parseInt(m[3]),
                jurisdictionVersion: parseInt(m[4]),
                numberOfEntries: parseInt(m[5])
            }
        };

     

        for (var i = 0; i < obj.header.numberOfEntries; i++) {
            var offset = 21 + i * 10;
            m = data.substring(offset, offset + 10).match(/(.{2})(\d{4})(\d{4})/);
            var subfileType = m[1];
            var offset = parseInt(m[2]);
            var length = parseInt(m[3]);
            if (i === 0) {
              obj.files = [ subfileType ];
            } else {
              obj.files.push(subfileType);
            }
            obj[subfileType] = data.substring(offset + 2, offset + length - 1).split("\n").reduce(function (p, c) {
                p[c.substring(0,3)] = c.substring(3);
                return p;
            }, { } );
        }

     

        if (obj.DL) {
            ["DBA", "DBB", "DBD", "DDB", "DDC", "DDH", "DDI", "DDJ"].forEach(function (k) {
                if (!obj.DL) return;
                m = obj.DL.match(/(\d{2})(\d{2})(\d{4})/);
                if (!m) return;
                obj.DL = (new Date(m[3] + "-" + m[1] + "-" + m[2])).getTime();
            } );
        }

     

        return JSON.stringify(obj);
    }

 

LMedeirosUI
Occasional Contributor

@bsklaohfl I think the issue is because you haven't told the JSON to search for anything.

In the example in the blog, the pulldata function searches the ${myjson} result using DL.DAC

Try adding that to your XLS form (see the exact example below)

textdnameNamepulldata("@json",${myjson},"DL.DAC")

 

Or are you getting the error message in the JS tab or after saving the form it displays in the subsequent fields (when testing in Connect)?

<update> After looking into this more, I can't get this to work either. I'm not getting your error message, but the javascript won't pull everything out. I'm not sure what's going on. If the suggestion above works for you, great! If not... sorry and I hope you get it working!

JudithLandry
New Contributor II

Hi @IsmaelChivite,

Since the last release, repeats in JS functions don’t behave as Arrays anymore. In the example below, you can see that the value of each question differs depending on how you calculate them.

Questions 1 and 2 have the same results for both calculations but 3 and 4 have wrong calculations when using JS. Since questions 3 and 4 are unanswered in the first repeat, we should have “undefined” as values. Instead, the values of the second repeat are shifted to the questions of the first repeat.

JudithLandry_0-1686139405765.png

Will this behavior be the norm from now on?

JamesTedrick
Esri Esteemed Contributor

Hi @JudithLandry -

Apologies for the delay, we just noticed this comment.  I think I see the issue you are describing - to confirm, this is not when you are passing the entire repeat set into a function, but you are passing a field within the repeat into the function - undefined values are being stripped from the set of values?

 

JudithLandry
New Contributor II

HI @JamesTedrick ,

Yes, for a given question, undefined value is replaced by the value of the next repeat (same question) if there is, and it gives no answers (blank) in the second repeat.

I didn’t check if this issue was corrected in the lastest release.

 

DerekKonofalski
Occasional Contributor

Hi, @IsmaelChivite and @JamesTedrick!

I love the ability to use JS in our forms (even though you took away Survey123 Connect for MacOS 😘).

A few questions:

1. Is it possible not to submit the @pulldata("javascript") fields when submitting the form? I have a @pulldata("javascript") field along with some @pulldata("json") fields that grab the information we need into the respective inputs. I've used the "hidden" appearance in the XLSForm to prevent the returned response from showing but it's getting submitted with the rest of the fields, even though I don't need it. Is there any way to prevent that?

2. Along the same lines, putting a response into a field using @pulldata("javascript") will often return different responses depending on what's being pulled. In the VIN sample you provide, the response is predicated on which VIN is entered. My issue is that, depending on what info we're pulling, the variable length of the JSON response causes the submission to fail if the response is greater than 255 characters. I realize that I can change the field length to accommodate larger responses but, since I don't know how long the response could be in some cases, how do I avoid this if I can't prevent the fields from being submitted with the form. Worse yet, it took me hours to figure this out since the field was hidden and the validation simply fails with "Please fix the errors with the form. Click here to jump to the bad question" (to paraphrase, I don't remember the exact text). Since the field is hidden, users can't ever submit the form because there's no way for them to fix the error.

3. I'm not able to access previous @pulldata("javascript") fields in others. Some of the fields that I'd normally use @pulldata("json") calculations for need to be converted to title case. I know that this isn't possible with the @pulldata("json") function so I attempted to pass my previous response to a new @pulldata("javascript") function. No matter what I tried, though, including the handy `${data_response}` syntax, I can't get the old response to parse any of the response. I tried JSON.parse(data_response) but that also fails with an error that it's unable to parse.

This is really great functionality so I'm so very thankful for it. Just trying to wrap my head around some of the limits of the JS that I don't normally have working in a web browser. You all rock!

EvanR
by
New Contributor III

@DerekKonofalski, for your first issue, have you tried putting "null" in the bind::esri:fieldType column for that question? That should prevent the field from being reported to the feature service when you submit a survey response (and will prevent the field from being included in the feature service at all if you have it set that way before publishing initially). Apologies if I am misunderstanding the question.

DerekKonofalski
Occasional Contributor

@EvanR - I'll have to try that again. I think I did try it initially but started getting errors with the response after doing so. I'm not certain that my errors weren't related to something else so I'll try that again now that I have a working JS script.

DerekKonofalski
Occasional Contributor

@EvanR - That didn't work. It still submitted the results from that field. Thanks for the attempt, though!

MarcMuehleisen
New Contributor

Hello everyone and @IsmaelChivite,

i am using this approach to calculate the closest point of a feature service depending on the chosen location in my survey.

Now i need to extent this functionality with a where clause that filters the feature service according to a parameter that gets calculated in Survey123. In my case called ${road}.

Do you see any reason why the where clause "road='${road}'" would not work? If i exchange it to a real value of the feature service like "road='50428'", it is working just fine. Is it maybe because initially ${road} is empty? Tried it with if($road}!="".....), but that did not work.

My js pulldata function looks like: pulldata("@javascript","myJSFile.js","myFunction",${location},"parameter1", "road='${road}'", parameter2)

Thank you in advance!

MarcMuehleisen
New Contributor

I was able to solve my problem by adding a calculation row to calculate the value of the where clause as follows: concat("road='",${road},"'")

DerekKonofalski
Occasional Contributor

@MarcMuehleisen - Are you sure you're setting that up right? I don't think you need a calculation row. I think you just need to set your @pulldata function call in the Excel sheet to pulldata("@javascript","myJSFile.js","myFunction",${location},"parameter1", ${road}, "parameter2") and then, in your JS file, use the literals function to set the 'road' variable. Literals use the backtick character - ` - rather than the standard double or single quotes. That allows you to read inputs from your form, using the XLSForm field name.

EricaNova
Occasional Contributor

If I edit my script, and save it, do I need to re-publish my survey to ensure the edits will be respected?

DerekKonofalski
Occasional Contributor

@EricaE - Yes. Saving the script in S123 Connect only saves it locally to your computer. You need to publish it to have the files uploaded to the S123 site.