Write JavaScript

This action executes javascript code on a website.

References to Variables and Data Tables

Use a reference from a variable or a data table:

await getRef({ ref_id: 123, name: 'column or variable name' });

Save a value to a variable or a data table:

await setRef({ ref_id: 123, name: 'column or variable name', value: 'something' });

What is ref_id and where to find it?

ref_id corresponds to the data table id or variables id. In ZeroWork, data tables and variables are considered data groups. Each data group has its own id (which is ref_id), so ref_id corresponds to the whole data table id or to all variables. The individual columns and individual variables are uniquely identified by name.

Data table ids can be found on the left sidebar:

// example: get value from a data table
const profileName = await getRef({ ref_id: 4372, name: 'Profile Name' });

Variables id can be found in the variables dialog:

// example: save value to a variable
// note: all variables (both 'counter' and 'date') will have the same ref_id

const counter = await getRef({ ref_id: 4379, name: 'counter' });
const date = await getRef({ ref_id: 4379, name: 'date' });

Examples

Using browser in-built functions:

const selector = document.querySelector("div button");
if (selector) selector.click();

Making a fetch request, which returns an array and saving a value from the first item to a variable:

const res = await fetch(
'https://api.github.com/repos/microsoft/vscode-edge-devtools/issues?state=all&per_page=50&page=1'
);
const resData = await response.json();

// save to a variable
await setRef({ ref_id: 4379, name: "value from 1st item", value: resData[0]["title"] });

Dos and Don'ts

  1. Both getRef() and setRef() are executed asynchronously. Make sure to wait for response, for example, by using await. Otherwise the code will continue executing without waiting for response.

Won't work

const profileName = getRef({ ref_id: 4372, name: 'Profile Name' });
await setRef({ ref_id: 4372, name: 'New Profile Name', value: profileName });
// since await is missing in getRef, saves [object Object], and it's not what we want

Works

const profileName = await getRef({ ref_id: 4372, name: 'Profile Name' });
await setRef({ ref_id: 4372, name: 'copied profile name', value: profileName });
// properly saves profile name (e.g. "Jane Doe")
  1. Variable and column names are case-sensitive. For example, if the column in your data table is named "Profile Name" and you reference to it in lower case, you will get an error.

Won't work

const profileName = await getRef({ ref_id: 4372, name: 'profile name' });
// throws error: Cannot access the value of the variable or column. 
// This variable or column does not exist on this workflow

Works

const profileName = await getRef({ ref_id: 4372, name: 'Profile Name' });
  1. Ensure to use correct data types. ref_id is expected to be number or string; name and value are expected to be strings.

Won't work

const profileName = await getRef({ ref_id: 4372, name: address });
// throws error: ReferenceError: address is not defined

Works

const profileName = await getRef({ ref_id: 4372, name: 'address' });

Also works

const columnName = 'address';
const profileName = await getRef({ ref_id: 4372, name: columnName });

Saving Non-String Values

Whenever you use setRef(), the value is stringified when it's saved. If the value is, for example, array or object and you need to access it later when calling getRef(), you need to parse it first to json. See example below.

Assume you need to access the first item's object from the array saved to the variable "animals":

Won't work

const animals = await getRef({ ref_id: 4379, name: "animals" });
const firstAnimal = animals[0]["product"]; // resolves to undefined
await setRef({ ref_id: 4379, name: "first animal", value: firstAnimal });
// throws error: Required arguments ref_id, name or value are missing

Works

const animals = JSON.parse( await getRef({ ref_id: 4379, name: "animals" }) );
const firstAnimal = animals[0]["product"]; // resolves to "cat"
await setRef({ ref_id: 4379, name: "first animal", value: firstAnimal });

Saving or Getting Data Within Loops

getRef() and setRef() adhere to ZeroWork in-built loop logic. For example, in order to access value from a data table, you need to place Start Repeat building block with dynamic loop type before the Write JavaScript building block.

If you want to append data to a data table, you need to place Start Repeat building block with standard loop type before the Write JavaScript building block.

Custom Append Loop Logic without Start Repeat - advanced use

You can append rows within a custom loop in your code block by providing an argument for appendIndex.

Beware:

  • appendIndex refers to the row number that should be appended. appendIndex: 0 means the first row to be appended. If there are already 3 rows in the data table, it will be appended as 4th.

  • When using appendIndex, avoid placing Write JavaScript building block inside any ZeroWork in-built loop (by this, Start Repeat building block is meant) - whether nested or not! Otherwise the row indices might get mixed up between ZeroWork in-built loop logic and your custom loop logic, and you might end up with mismatched data.

With that in mind, here is an example of how you can make this scenario work:

And here is the end result:

Custom looping through existing rows of a data table within your code block in order to update data (as opposed to append) is currently not supported. However, in most use cases, adding dynamic loop with Start Repeat is enough to enable this use case. Please let us know if you want this feature to be implemented in our Discord server (#ideas-features-suggestions) while providing an example what you would need it for.

Log to TaskBot Logs in Reports

You can define your own log messages by using the function log.

await log({ message: "Logging something custom" });

Will be shown in your logs like so:

You can include status parameter, which is optional and accepts "success" or "fail". The default is "success". Must be either "fail" or "success".

await log({ message: "Logging something that went wrong", status: "fail" });

Will be shown in your logs highlighted in red:

You can include tag parameter, which is optional and makes your log message appear with a labeled tag in your logs. Must be string.

await log({ message: "Logging something custom", tag: "My Custom Tag" });

Will be shown in your logs with a tag on the right:

Custom Error Handling

ZeroWork in-built error handling behavior will not always apply to your custom code. For example, if you use building block Click Web Element and the selector is not found, normally you will get an error that selector could not be found (after the corresponding timeout) in your logs and the TaskBot run will be interrupted. However, because browser console treats not found selectors differently (by simply returning null), your TaskBot will continue its run despite the error.

Not recommended

document.querySelector("div button").click();
// might throw error: cannot read properties of null (reading 'click')

Better

const selector = document.querySelector("div button");
if (selector) selector.click();
// alternatively, use custom try-catch logic, see below

Define custom error messages

You can use custom error handling including custom try-catch logic and define custom behavior on error as well as your own custom error messages. Your custom error messages will be reflected in the logs and error reports. Consider the example below:

const selector = document.querySelector("div button");
if (!selector) throw new Error("My custom error message: div button is not found!");

This is how your custom error message in the logs and error reports will appear:

Here is another example with your own custom try-catch logic:

const doSomething = async (timeout) => {
    // some code that relies on timeout (e.g. making http request)
};

let timeOut = 10; // corresponds to 10 sec
try {
    await doSomething(timeOut);
} catch (e) {
    timeOut = timeOut + 10;
    if (e.message.includes("timeout" && timeOut < 50) await doSomething(timeOut);
    else throw e;
}

// the error message from e.message will be saved to the logs and the error reports

Using In-Built References Instead of getRef()

You might be used to inserting references directly from the dialog options. By this, we are referring to these options:

You can continue using these options. However, there are two caveats:

  1. The values are replaced before the code is executed. If you need dynamic getting and setting behavior within your code, you need to use getRef() and setRef().

  2. Beware to not change the way references are pasted when using these options - for example, when using these in-built options, the name is not in string. This is intended and not be confused with the arguments used in getRef().

Last updated