Select to view content in your preferred language

External Arcade scripts

2863
8
Jump to solution
09-18-2020 09:11 AM
KenBuja
MVP Esteemed Contributor

The samples for using Arcade scripts all use in-line scripts. Instead of having one big index.html file, are there any samples available showing how to keep these Arcade scripts in a separate location?

A followup question would be how to do this in TypeScript.

0 Kudos
1 Solution

Accepted Solutions
KristianEkenes
Esri Regular Contributor

Hi Ken,

This app stores most of the Arcade scripts in a different location: GitHub - ekenes/covid19viz: Visualization of COVID-19 cases over time. 

More specifically here: covid19viz/expressionUtils.ts at master · ekenes/covid19viz · GitHub 

And it's written in TypeScript. Hopefully that helps.

Kristian

View solution in original post

8 Replies
KristianEkenes
Esri Regular Contributor

Hi Ken,

This app stores most of the Arcade scripts in a different location: GitHub - ekenes/covid19viz: Visualization of COVID-19 cases over time. 

More specifically here: covid19viz/expressionUtils.ts at master · ekenes/covid19viz · GitHub 

And it's written in TypeScript. Hopefully that helps.

Kristian

KenBuja
MVP Esteemed Contributor

In the expressionUtils.ts, you use the backtick to enclose the actual Arcade code. What do you do in cases where the code is using template literals like in this sample?

0 Kudos
KristianEkenes
Esri Regular Contributor

You can use escape characters. This is documented on this page: Arcade | ArcGIS API for JavaScript 4.16  beginning with the line: "You can use template literals (ES6 or later) to write multi-line Arcade expressions."

For example:

layer.popupTemplate = {
  content: "{expression/percent-unemployed}",
  expressionInfos: [
    {
      name: "top-crimes",
      title: "Top crimes",
      expression: `
        var unemploymentRate = ( $feature.UNEMP_CY / $feature.LABOR_FORCE ) * 100;
        var population = $feature.POP_16UP;
        var populationNotWorking = ( ( $feature.UNEMP_CY + $feature.NOT_LABORFORCE_16 ) / $feature.POP_16UP ) * 100;

        // returns a string built using an Arcade template literal

        return \`\${$feature.COUNTY} County

        - Unemployment rate: \${Text(unemploymentRate, "##.#")}%
        - % population not working: \${Text(populationNotWorking, "##.#")}%
        - Population: \${Text(population, "#,###")}\`
      `
    }
  ]
}
0 Kudos
KenBuja
MVP Esteemed Contributor

However, when I use the "Summarize intersecting point" script, I get an error.

Uncaught ReferenceError: num is not defined at size.js:40

// Query the number of crimes that intersect a selected polygon
const base = `
  var crimes = Intersects(
    $feature,
    FeatureSetByName($map, "San Diego crimes", ["desc_", "is_night"])
  );

  // Queries the count of crimes grouped by the "desc_" field
  var stats = GroupBy(crimes, ["desc_"],
    [{ name: "total", expression: "1", statistic: "count" },
    { name: "night_avg", expression: "is_night", statistic: "avg" }
    ]
  );

  // Orders the results in descending order by the total count
  // excludes crimes that don't have a classification
  var topCrimes = Top(OrderBy(Filter(stats, "desc_ <> ''"), "total desc"), 3);

  var output = "";
  if (Count(topCrimes) == 0) {
    return "No crimes committed in this area";
  }
  var num = 0;
  // Format the results for display
  for (var item in topCrimes) {
    num++;
    var num_crimes = item.total;
    var crimeType = item["desc_"];

    // The isNight field has values of either 1 or 0.
    // If the average value is high, then most crimes
    // occurred at night. If the average is low, then
    // the crimes typically occurred during daytime hours.
    var timeOfDay = When(
      item.night_avg >= 0.6, "at night",
      item.night_avg <= 0.4, " during the daytime hours",
      " at both night and day");

    // Display crime type with count using template literals
    output += \`${num}. ${crimeType}
    -- Total offenses: ${Text(num_crimes, "#,###")}
    -- Most crimes were reported ${timeOfDay}

\`;
  }
  return output;
`;
0 Kudos
KristianEkenes
Esri Regular Contributor

You need to escape the $ characters as well.

0 Kudos
RyanBohan
Frequent Contributor

Thank you for calling it a backtick.  I hate to say how long I spent trying to use a single quote to get an sample arcade to run.  If your looking for it the backtick ` is top left on most keyboards

KenBuja
MVP Esteemed Contributor

Thanks for your help, Kristian. I have been able to incorporate the external scripts into my code. Here's one of the scripts in my external file (scripts.ts).

export function getSources() {
  return `
  var documents = FeatureSetByRelationshipName($feature, "Document");

  // initialize result text and get count
  var result = "";
  var cnt = Count(documents);
  // check count and start creating result text
  if (cnt > 0) {
    // we have related records
    result = cnt + " documents found:";
    // loop through venues
    for (var document in documents) {
      result += TextFormatting.NewLine + " - " + document["Title"];
      // add a line per venue and include name and address
    }
  } else {
    // we don't have related record, show that in the result
    result = "No documents found";
  }

  return result;
`;
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

And here's how I bring it into my code

import { getSources } from './arcade/scripts'

const regionLayer = new FeatureLayer({
  url: '...',
  popupTemplate: {
    title: 'Documents by region',
    content: [
      {
        type: 'text',
        text: [
          '{expression/doc-count}'
        ]
      }
    ],
    expressionInfos: [
      {
        name: 'doc-count',
        title: 'Document Count',
        expression: getSources()
      }]
  },
  outFields: ['*']
});‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Now I have a followup question. I'd like to use html formatting in my returned list, using this script.

export function getSourcesList() {
  return `
  var documents = FeatureSetByRelationshipName($feature, "Document");

  // initialize result text and get count
  var result = "";
  var cnt = Count(documents);

  // check count and start creating result text
  if (cnt > 0) {
    // we have related records
    result = \`There are a total of \${ cnt }  documents found: <ul>\`;
    // loop through venues
    for (var document in documents) {
      // add a line per venue and include name and address
      result += \`<li>\${document["Title"]}</li>\`;
    }
    result += \`</ul>\`; 
    } else {
    // we don't have related record, show that in the result
    result = "No documents found";
  }
  
  return result;
`;
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

If I use this script with the ExpressionInfos and the text property, I see the formatting tags.

It looks like I have to use CustomContent for this to appear with the formatting tags, but I'm just getting the entire script returned, not the results of the script.

  const contentDocList = new CustomContent({
    outFields: ['*'],
    creator: (event) => getSourcesList()
  });

  const regionLayer = new FeatureLayer({
    url: '...',
    popupTemplate: {
      title: 'Documents by region',
      content: [
        contentDocList
      ],
    outFields: ['*']
  });‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

What's the proper way to do this?

0 Kudos
KristianEkenes
Esri Regular Contributor

Yeah, this is an Arcade limitation for now. Arcade in popups only supports returning numbers or strings, not HTML markup. It's been on our list for some time to evaluate this. It's a popular request. So unfortunately we don't support this at the moment. 

Kristian