Skip to main content
CYPEX Documentation
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage Support

Built-in expressions

CYPEX built-in expressions

Let’s come to a core concept of CYPEX: expressions. What you actually see in the GUI isn’t just some static field, but in fact, a JavaScript expression which can be modified. This gives you a great deal of flexibility and allows you to tailor the GUI to your needs. Using JavaScript expressions, you have great power at your fingertips.

However, most people aren’t heavy JavaScript users and therefore a lot of the more common tasks have been simplified by adding graphical shortcuts. One of these shortcuts was shown before: ID resolution. What the GUI element actually does is to modify the underlying JavaScript relation in the desired way.

But let’s not get lost in technical details, rather move forward and see what you can do in real life to build more useful applications.

expressions

To show how things work, look at the TODO list built in one of the previous chapters. Go to edit mode, and click on the “TODO item” column in your main table. If you look closely, you’ll see “props.data”. This is the JavaScript expression mentioned a moment ago.

In this case, the column is supposed to display the “todo_item” element coming from the backend. But you can modify that - you can apply basically any expression to this data.

For the sake of simplicity, let’s add a prefix to the content of the column:

expressions

What we’re using here is pure JavaScript code.

“MY CHANGE: “ will be used as a prefix. If you have a basic knowledge of JavaScript, you’ll be able to do really powerful things using simple expressions.

The final product will look as follows:

expressions

The way data is displayed has been changed on the fly. The workflow stays unchanged. The data in the backend is also going to stay unchanged - we’re only talking about the way CYPEX displays data.

CYPEX Custom Expressions

Custom expressions allow you to write JavaScript code that evaluates at runtime, enabling dynamic behavior in your CYPEX applications. Instead of static values, you can create expressions that:

  • Display data from other elements
  • Calculate values dynamically
  • Filter data based on user input
  • Format content conditionally
  • Show or hide elements based on conditions
  • Create interactive user experiences

Where Can I Use Custom Expressions?

Custom expressions are available in many element configuration fields. Look for the expression toggle (⚡) icon next to input fields in the element editor. Common use cases include:

  • Text Fields: Dynamic text content (text property)
  • Data Display: Custom formatting (format property)
  • Tables: Filter expressions (fixedFilter, elementsFilterCombinator, hidden columns)
  • Meta Query: Dynamic filters (filter property)
  • Number Fields: Calculated values (value property)
  • Call Buttons: Dynamic function arguments (arguments property)
  • Form Elements: Conditional visibility and dynamic values
  • Color Fields: Dynamic colors based on data (color, background properties)

Tip: If a field supports custom expressions, you’ll see a toggle switch (⚡) in the editor. Click it to switch between “Plain Value” and “Expression” mode.

Understanding Selectors and Context

In CYPEX, each element on the page has access to its selectors - predefined JavaScript objects with properties and values that allow elements to interact with each other in a controlled way.

Available Context Objects:

  • element: The current element’s own data and properties
  • elements: Access to all other elements on the page (e.g., elements["input_id"].value)
  • props: Data passed from parent elements (e.g., table rows, form data)
  • location: Current page URL and query parameters
  • page: Current page information
  • i18n: Translation and internationalization
  • moment: Date/time manipulation library
  • lodash: Utility functions library

Depending on the element configuration, custom expressions should return the appropriate value type: string, number, boolean, array, object, or function. We recommend checking out our video tutorial series for visual examples.

Custom Expression Format

Custom expressions in CYPEX are JavaScript code that evaluates at runtime. When you enter a value in a field that supports custom expressions, you can toggle between a plain value and an expression using the toggle switch in the editor. The expression prefix (@@expression:) is handled automatically by CYPEX - you just write the JavaScript code.

Plain Value:

Hello World

Custom Expression:

"Hello " + "World"

Note: You don’t need to type @@expression: manually. The expression editor adds this automatically when you toggle to expression mode.

Quick Start: How to Use Custom Expressions

  1. Open an element editor - Click on any element in edit mode
  2. Find a field with the expression toggle - Look for the ⚡ icon next to input fields
  3. Click the toggle - Switch to “Expression” mode
  4. Enter your JavaScript code - Type your expression (no prefix needed - CYPEX handles this automatically)
  5. Test your expression - The result is evaluated in real-time

Simple Custom Expression Examples

Here are common, simple use cases for custom expressions:

1. Displaying Element Values

Example: Display a number field’s value with currency formatting

1
element.data.value + " USD";

Example: Access a table column value

1
props.data["first_name"] + " " + props.data["last_name"];

2. Conditional Text Display

Example: Show different text based on a condition

1
element.data.status === "active" ? "Active" : "Inactive";

Example: Display a message based on another element’s value

1
2
3
elements["status_dropdown"].value === "approved"
  ? "Approved!"
  : "Pending Review";

3. Accessing URL Parameters

Example: Use a query parameter from the URL

1
location.query.identifier || "default-value";

Example: Build a dynamic identifier from URL

1
location.query.id ? `Record ${location.query.id}` : "New Record";

4. Using Translations

Example: Display translated text

1
i18n.text;

Example: Combine translation with dynamic value

1
i18n.label + ": " + element.data.value;

5. Simple Calculations

Example: Calculate a total

1
props.data["quantity"] * props.data["price"];

Example: Calculate percentage

1
(element.data.completed / element.data.total) * 100 + "%";

6. String Manipulation

Example: Format a date string

1
moment(props.data["created_at"]).format("YYYY-MM-DD");

Example: Uppercase conversion

1
props.data["name"].toUpperCase();

7. Boolean Conditions

Example: Check if a value exists

1
!!elements["input_field"].value;

Example: Compare two values

1
element.data.value > 100;

Advanced Custom Expression Examples

For more complex scenarios, custom expressions can handle sophisticated logic:

1. Complex Data Formatting

Example: Format a data display with multiple fields

1
`${props.data["first_name"]} ${props.data["last_name"]} (${props.data["email"]})`;

Example: Format currency with proper decimal places

1
2
3
new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(
  props.data["amount"],
);

2. Array Operations

Example: Count items in an array

1
element.data.items ? element.data.items.length : 0;

Example: Filter and map array data

1
2
3
4
element.data.items
  .filter((item) => item.status === "active")
  .map((item) => item.name)
  .join(", ");

3. Complex Conditional Logic

Example: Multi-condition display

1
2
3
4
5
6
7
element.data.status === "pending"
  ? "Awaiting Review"
  : element.data.status === "approved"
    ? "Approved"
    : element.data.status === "rejected"
      ? "Rejected"
      : "Unknown Status";

Example: Conditional styling based on multiple values

1
2
3
4
5
element.data.score >= 90
  ? "green"
  : element.data.score >= 70
    ? "yellow"
    : "red";

4. Table Filter Expressions

Example: Dynamic filter based on element values

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "combinator": "AND",
  "filters": [
    {
      "field": "status",
      "operator": "eq",
      "value": elements["status_filter"].value
    },
    {
      "field": "created_at",
      "operator": "gte",
      "value": elements["date_from"].value
    }
  ]
}

Example: Filter using selected table row

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "category_id",
      "operator": "eq",
      "value": elements["categories_table"].selected?.row?.id
    }
  ]
}

5. Function Expressions

Example: GeoJSON style function

1
2
3
4
5
6
7
8
9
(feature) => {
  if (feature.properties.status === "active") {
    return { color: "green", weight: 3 };
  } else if (feature.properties.status === "warning") {
    return { color: "yellow", weight: 2 };
  } else {
    return { color: "red", weight: 1 };
  }
};

Example: Custom formatting function

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
(value) => {
  if (!value) return "N/A";
  const date = moment(value);
  const now = moment();
  const diffDays = now.diff(date, "days");
  if (diffDays === 0) return "Today";
  if (diffDays === 1) return "Yesterday";
  if (diffDays < 7) return `${diffDays} days ago`;
  return date.format("YYYY-MM-DD");
};

6. Using Lodash Utilities

Example: Group and count data

1
_.size(_.groupBy(element.data.items, "category"));

Example: Find and format

1
_.get(element.data, "user.profile.name", "Anonymous");

Example: Debounce-like logic with lodash

1
2
3
_.isEmpty(elements["search_input"].value)
  ? "All Items"
  : `Search: ${elements["search_input"].value}`;

7. Call Button Arguments

Example: Dynamic function call with computed arguments

1
2
3
4
5
6
{
  "record_id": elements["table"].selected?.row?.id,
  "action": "update",
  "timestamp": moment().toISOString(),
  "user_id": user.id
}

Example: Conditional function name

1
elements["mode_toggle"].value === "create" ? "createRecord" : "updateRecord";

8. Data Display Format Expressions

Example: Format with conditional logic

1
2
3
element.data.value
  ? `${element.data.value} ${element.data.unit || ""}`.trim()
  : "No value available";

Example: Format with error handling

1
2
3
4
5
element.error
  ? `Error: ${element.error}`
  : element.loading
    ? "Loading..."
    : (element.data?.value ?? "No data");

9. Complex String Building

Example: Build URL with query parameters

1
`/details?id=${location.query.id}&mode=${elements["mode_selector"].value}`;

Example: Build email subject line

1
`Re: ${props.data["subject"]} - ${props.data["ticket_number"]}`;

10. Date and Time Calculations

Example: Calculate age from birthdate

1
moment().diff(moment(props.data["birthdate"]), "years");

Example: Show relative time

1
moment(props.data["created_at"]).fromNow();

Example: Format date range

1
`${moment(props.data["start_date"]).format("MMM D")} - ${moment(props.data["end_date"]).format("MMM D, YYYY")}`;

Element-Specific Expression Examples

This section provides practical examples for using custom expressions in specific CYPEX elements.

Text Field

Property: text - Returns string, number, or null

Example: Display dynamic text

1
`Welcome, ${elements["user_name_input"].value || "Guest"}!`;

Example: Conditional text based on data

1
props.data["status"] === "active" ? "✓ Active" : "✗ Inactive";

Example: Format with currency

1
`Total: ${new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(props.data["amount"])}`;

Data Display

Property: format - Returns any value for display formatting

Example: Format multiple fields

1
`${props.data["first_name"]} ${props.data["last_name"]}\n${props.data["email"]}`;

Example: Format with conditional logic

1
2
3
element.data.value
  ? `${element.data.value} ${element.data.unit || ""}`.trim()
  : "No value available";

Example: Custom query filter

1
2
3
location.query.category_id
  ? `?category_id=eq.${location.query.category_id}`
  : "";

Table

Properties: fixedFilter, elementsFilterCombinator, hidden (for columns)

Example: Fixed filter with multiple conditions

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
(() => {
  const status = elements["status_filter"].value;
  const dateFrom = elements["date_from"].value;
  const filters = [];

  if (status) {
    filters.push({ field: "status", operator: "eq", value: status });
  }

  if (dateFrom) {
    filters.push({
      field: "created_at",
      operator: "gte",
      value: moment(dateFrom).startOf("day").toISOString(),
    });
  }

  return { combinator: "AND", filters: filters };
})();

Example: Hide column conditionally

1
!elements["admin_toggle"].value;

Example: Filter combinator based on selection

1
elements["filter_mode"].value === "any" ? "OR" : "AND";

Meta Query

Property: filter - Returns PostgREST filter object

Example: Dynamic filter from form inputs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(() => {
  const search = elements["search_input"].value;
  const category = elements["category_select"].value;
  const filters = [];

  if (search) {
    filters.push({
      field: "name",
      operator: "ilike",
      value: "%" + search + "%",
    });
  }

  if (category) {
    filters.push({
      field: "category_id",
      operator: "eq",
      value: category,
    });
  }

  return { combinator: "AND", filters: filters };
})();

Number Field

Property: value - Returns number

Example: Calculate from other elements

1
(elements["quantity_input"].value || 0) * (elements["price_input"].value || 0);

Example: Sum array values

1
element.data.items?.reduce((sum, item) => sum + (item.amount || 0), 0) || 0;

Call Button

Property: arguments - Returns object with function arguments

Example: Dynamic function arguments

1
2
3
4
5
{
  recordId: props.data["id"],
  action: elements["action_selector"].value,
  timestamp: moment().toISOString(),
};

Example: Conditional arguments

1
2
3
4
5
6
7
elements["mode_toggle"].value === "create"
  ? { type: "createRecord", data: elements["form"].data }
  : {
      type: "updateRecord",
      id: location.query.id,
      data: elements["form"].data,
    };

Color Field / Background

Properties: color, background - Returns color string

Example: Color based on status

1
2
3
4
5
element.data.status === "active"
  ? "green"
  : element.data.status === "warning"
    ? "yellow"
    : "red";

Example: Color based on value threshold

1
2
3
4
5
props.data["score"] >= 90
  ? "#4caf50"
  : props.data["score"] >= 70
    ? "#ff9800"
    : "#f44336";

Conditional Container

Property: visible - Returns boolean

Example: Show based on user role

1
elements["user_profile"].data?.role === "admin";

Example: Show based on form state

1
elements["form"].hasChanges && elements["form"].isValid;

Form Identifier Value

Property: identifierValue - Returns string or null

Example: Get ID from URL

1
location.query.id || null;

Example: Get ID from selected table row

1
elements["records_table"].selected?.row?.id || null;

Quick Reference

Common Variable Access Patterns

PatternDescriptionExample
element.data.valueCurrent element’s data valueelement.data.value
elements["id"].valueAnother element’s valueelements["input_123"].value
props.data["field"]Table row or form field dataprops.data["first_name"]
location.query.paramURL query parameterlocation.query.id
i18n.labelTranslated labeli18n.label
moment().format()Date formattingmoment().format("YYYY-MM-DD")

Common Expression Patterns

Use CasePatternExample
Conditional valuecondition ? value1 : value2status === "active" ? "Yes" : "No"
Null fallbackvalue ?? defaultValueelement.data.value ?? "N/A"
Safe property accessobject?.propertyelements["table"].selected?.row?.id
String concatenation`${var1} ${var2}``Hello ${name}`
Array length checkarray?.length > 0element.data.items?.length > 0
Date comparisonmoment(date1).isAfter(date2)moment(date).isAfter(moment())

PostgREST Filter Quick Reference

OperatorUse CaseExample
eqExact match{ field: "status", operator: "eq", value: "active" }
ilikeCase-insensitive search{ field: "name", operator: "ilike", value: "%john%" }
inMatch any in list{ field: "id", operator: "in", value: [1, 2, 3] }
gte / lteRange queries{ field: "price", operator: "gte", value: 100 }
csArray contains all{ field: "tags", operator: "cs", value: ["a", "b"] }
ovArray overlap{ field: "categories", operator: "ov", value: [1, 2] }
isNull check{ field: "deleted_at", operator: "is", value: null }

Self-Calling Function Pattern for Filters

Use this pattern when building filters with optional values:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
(() => {
  const value1 = elements["input1"].value;
  const value2 = elements["input2"].value;
  const filters = [];

  if (value1) {
    filters.push({ field: "field1", operator: "eq", value: value1 });
  }

  if (value2) {
    filters.push({ field: "field2", operator: "eq", value: value2 });
  }

  return { combinator: "AND", filters: filters };
})();

Best Practices

  1. Use Optional Chaining: Always use ?. when accessing nested properties that might not exist:

    1
    
    elements["table"].selected?.row?.id ?? "none";
    
  2. Provide Fallbacks: Use nullish coalescing (??) or ternary operators for default values:

    1
    
    element.data.value ?? "Default Value";
    
  3. Handle Loading States: Check for loading/error states when accessing data:

    1
    
    element.loading ? "Loading..." : (element.data?.value ?? "No data");
    
  4. Keep Expressions Readable: Break complex logic into multiple expressions or use clear variable names in comments (though comments aren’t supported, keep logic simple).

  5. Validate Input: Check for null/undefined before performing operations:

    1
    
    props.data["amount"] ? props.data["amount"] * 1.1 : 0;
    
  6. Use Element IDs Correctly: Access other elements using their full ID:

    1
    
    elements["default_text_field_abc123"].value;
    

Common Pitfalls

  1. Forgetting to Toggle Expression Mode: Make sure to toggle to expression mode (⚡ icon) - the prefix is added automatically
  2. Accessing Non-existent Properties: Always check if properties exist before accessing
  3. Circular Dependencies: Avoid expressions that reference each other in a loop
  4. Returning Module Proxies: Never return element modules from expressions (they’re automatically detected and prevented)
  5. Type Mismatches: Ensure the expression returns the expected type (string, number, boolean, etc.)

Filtering Data Sources with PostgREST Filters

Custom expressions are particularly powerful when used to filter data sources like tables, data displays, and meta queries. CYPEX uses PostgREST filter syntax, which provides a comprehensive set of operators for querying your database.

Note: CYPEX implements a subset of PostgREST operators that are most commonly used. For the complete list of PostgREST operators, refer to the official PostgREST documentation.

Understanding PostgREST Filter Structure

PostgREST filters use a structured format with three main components:

  • combinator: How multiple filters are combined ("AND" or "OR")
  • filters: An array of filter rules
  • Each filter rule contains:
    • field: The database column name to filter on
    • operator: The comparison operator (e.g., "eq", "gte", "like")
    • value: The value to compare against (can be static or from an element)

Basic Filter Structure

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "column_name",
      "operator": "eq",
      "value": "filter_value"
    }
  ]
}

PostgREST Operators by Data Type

String Operators

OperatorDescriptionExample Use CasePostgREST Equivalent
eqEquals (exact match)Find records with specific name=
neqNot equalExclude specific values<> or !=
likePattern match (case-sensitive)Search with wildcards (%text%)LIKE
ilikePattern match (case-insensitive)Case-insensitive searchILIKE
matchRegular expression matchAdvanced pattern matching~
imatchCase-insensitive regex matchCase-insensitive regex~*
ftsFull-text searchPostgreSQL full-text search@@ (to_tsquery)
isIS comparison (null, true, false)Check for null valuesIS
inIn listMatch any value in an arrayIN

Note: For pattern matching with like and ilike, you can use % as a wildcard. In URLs, PostgREST also accepts * as an alias for % to avoid URL encoding, but in CYPEX filter expressions, use % directly.

Example: Filter by name using exact match

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "first_name",
      "operator": "eq",
      "value": elements["name_input"].value
    }
  ]
}

Example: Case-insensitive search with pattern matching

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "email",
      "operator": "ilike",
      "value": "%" + elements["search_input"].value + "%"
    }
  ]
}

Example: Full-text search

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "description",
      "operator": "fts",
      "value": elements["search_box"].value
    }
  ]
}

Example: Match multiple values (IN operator)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "status",
      "operator": "in",
      "value": ["active", "pending", "review"]
    }
  ]
}

Example: Check for null values

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "deleted_at",
      "operator": "is",
      "value": null
    }
  ]
}

Number Operators

OperatorDescriptionExample Use CasePostgREST Equivalent
eqEqualsExact number match=
neqNot equalExclude specific number<> or !=
gtGreater thanValues above threshold>
gteGreater than or equalValues at or above threshold>=
ltLess thanValues below threshold<
lteLess than or equalValues at or below threshold<=
isdistinctIs distinct fromHandles null comparisonsIS DISTINCT FROM
isIS comparisonCheck for nullIS
inIn listMatch any number in arrayIN
allAll values match (for arrays)Array field contains all valuesArray operator
anyAny value matches (for arrays)Array field contains any valueArray operator

Example: Filter by price range

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "combinator": "AND",
  "filters": [
    {
      "field": "price",
      "operator": "gte",
      "value": elements["min_price"].value
    },
    {
      "field": "price",
      "operator": "lte",
      "value": elements["max_price"].value
    }
  ]
}

Example: Filter by multiple numeric values

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "category_id",
      "operator": "in",
      "value": [1, 2, 3, 5, 8]
    }
  ]
}

Example: Filter using array field (any value matches)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "tags",
      "operator": "any",
      "value": elements["selected_tag"].value
    }
  ]
}

Date Operators

OperatorDescriptionExample Use CasePostgREST Equivalent
eqEqualsExact date match=
neqNot equalExclude specific date<> or !=
gtGreater thanDates after>
gteGreater than or equalDates on or after>=
ltLess thanDates before<
lteLess than or equalDates on or before<=
ovOverlapDate ranges that overlap&&
slStrictly left ofDate range completely before<<
srStrictly right ofDate range completely after>>
isIS comparisonCheck for null datesIS

Note: For date range operators (ov, sl, sr), use PostgreSQL range format: [start,end] for inclusive ranges or (start,end) for exclusive ranges. Example: "[2024-01-01,2024-12-31]".

Example: Filter by date range

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "combinator": "AND",
  "filters": [
    {
      "field": "created_at",
      "operator": "gte",
      "value": elements["date_from"].value
    },
    {
      "field": "created_at",
      "operator": "lte",
      "value": elements["date_to"].value
    }
  ]
}

Example: Filter records created today

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "combinator": "AND",
  "filters": [
    {
      "field": "created_at",
      "operator": "gte",
      "value": moment().startOf("day").toISOString()
    },
    {
      "field": "created_at",
      "operator": "lt",
      "value": moment().endOf("day").toISOString()
    }
  ]
}

Example: Filter by date overlap (for date ranges)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "event_period",
      "operator": "ov",
      "value": `[${elements["start_date"].value},${elements["end_date"].value}]`
    }
  ]
}

Boolean Operators

OperatorDescriptionExample Use CasePostgREST Equivalent
eqEqualsMatch true/false=
neqNot equalExclude true/false<> or !=
isIS comparisonCheck for nullIS

Note: Use is operator with null, true, or false values. Example: { field: "deleted_at", operator: "is", value: null } or { field: "is_active", operator: "is", value: true }.

Example: Filter active records only

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "is_active",
      "operator": "eq",
      "value": true
    }
  ]
}

Example: Filter using checkbox value

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "published",
      "operator": "eq",
      "value": elements["publish_checkbox"].value
    }
  ]
}

Array Operators

OperatorDescriptionExample Use CasePostgREST Equivalent
csContainsArray field contains all values@>
cdContained inArray field is subset of value<@
ovOverlapArrays have common elements&&
slStrictly left ofArray completely before<<
srStrictly right ofArray completely after>>
inIn listArray field value in listIN
allAll values matchAll array elements match conditionArray operator
anyAny value matchesAny array element matches conditionArray operator

Note:

  • cs (contains): Array field must contain all specified values. Example: { field: "tags", operator: "cs", value: ["important", "urgent"] } matches rows where tags array contains both “important” AND “urgent”.
  • ov (overlap): Array field has any common elements. Example: { field: "categories", operator: "ov", value: [1, 2, 3] } matches rows where categories array contains any of 1, 2, or 3.
  • Use curly braces {} for array values in filters: ["value1", "value2"] or [1, 2, 3].

Example: Filter by tags (array contains)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "tags",
      "operator": "cs",
      "value": ["important", "urgent"]
    }
  ]
}

Example: Filter where array contains any of the selected values

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "categories",
      "operator": "ov",
      "value": elements["selected_categories"].value
    }
  ]
}

Example: Filter using array input element

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "user_ids",
      "operator": "cs",
      "value": elements["user_multiselect"].value
    }
  ]
}

Example: Filter where array field contains all selected values

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "required_skills",
      "operator": "cs",
      "value": elements["skills_multiselect"].value
    }
  ]
}

Combining Multiple Filters

Using AND Combinator

All conditions must be true:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
  "combinator": "AND",
  "filters": [
    {
      "field": "status",
      "operator": "eq",
      "value": "active"
    },
    {
      "field": "created_at",
      "operator": "gte",
      "value": "2024-01-01"
    },
    {
      "field": "price",
      "operator": "lte",
      "value": 1000
    }
  ]
}

Using OR Combinator

At least one condition must be true:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "combinator": "OR",
  "filters": [
    {
      "field": "status",
      "operator": "eq",
      "value": "pending"
    },
    {
      "field": "status",
      "operator": "eq",
      "value": "review"
    }
  ]
}

Real-World Filtering Examples

Example 1: Multi-Field Search Form

Filter a table using multiple optional input fields. Use a self-calling function to define constants and only include filters when values exist (PostgREST is sensitive to null/undefined values):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
(() => {
  const firstName = elements["first_name_input"].value;
  const lastName = elements["last_name_input"].value;
  const email = elements["email_input"].value;
  const statuses = elements["status_multiselect"].value;

  const filters = [];

  if (firstName) {
    filters.push({
      field: "first_name",
      operator: "ilike",
      value: "%" + firstName + "%",
    });
  }

  if (lastName) {
    filters.push({
      field: "last_name",
      operator: "ilike",
      value: "%" + lastName + "%",
    });
  }

  if (email) {
    filters.push({
      field: "email",
      operator: "ilike",
      value: "%" + email + "%",
    });
  }

  if (statuses && Array.isArray(statuses) && statuses.length > 0) {
    filters.push({
      field: "status",
      operator: "in",
      value: statuses,
    });
  }

  return {
    combinator: "AND",
    filters: filters,
  };
})();

Example 2: Filter by Selected Table Row

Filter a related table based on the selected row in another table:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "category_id",
      "operator": "eq",
      "value": elements["categories_table"].selected?.row?.id
    }
  ]
}

Example 3: Dynamic Date Range Filter

Filter records within a date range with optional boundaries. Use a self-calling function to only include filters when dates are provided:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
(() => {
  const dateFrom = elements["date_from"].value;
  const dateTo = elements["date_to"].value;
  const filters = [];

  if (dateFrom) {
    filters.push({
      field: "created_at",
      operator: "gte",
      value: moment(dateFrom).startOf("day").toISOString(),
    });
  }

  if (dateTo) {
    filters.push({
      field: "created_at",
      operator: "lte",
      value: moment(dateTo).endOf("day").toISOString(),
    });
  }

  return {
    combinator: "AND",
    filters: filters,
  };
})();

Example 4: Complex Filter with Array Input

Filter where an array field contains values from a multi-select input:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "combinator": "AND",
  "filters": [
    {
      "field": "tags",
      "operator": "cs",
      "value": elements["tags_multiselect"].value || []
    },
    {
      "field": "status",
      "operator": "eq",
      "value": elements["status_dropdown"].value
    }
  ]
}

Example 5: Filter with Conditional Logic

Only apply certain filters when conditions are met. Use a self-calling function to safely handle optional filters:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
(() => {
  const showArchived = elements["show_archived"].value;
  const searchInput = elements["search_input"].value;

  const filters = [
    {
      field: "is_active",
      operator: "eq",
      value: true,
    },
  ];

  if (!showArchived) {
    filters.push({
      field: "archived_at",
      operator: "is",
      value: null,
    });
  }

  if (searchInput) {
    filters.push({
      field: "name",
      operator: "ilike",
      value: "%" + searchInput + "%",
    });
  }

  return {
    combinator: "AND",
    filters: filters,
  };
})();

Example 6: Price Range with Optional Filters

Filter products by price range, with optional category and search. Use a self-calling function to handle optional filters safely:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
(() => {
  const minPrice = elements["min_price"].value;
  const maxPrice = elements["max_price"].value;
  const category = elements["category_select"].value;
  const search = elements["search_input"].value;

  const filters = [];

  if (minPrice) {
    filters.push({
      field: "price",
      operator: "gte",
      value: minPrice,
    });
  }

  if (maxPrice) {
    filters.push({
      field: "price",
      operator: "lte",
      value: maxPrice,
    });
  }

  if (category) {
    filters.push({
      field: "category_id",
      operator: "eq",
      value: category,
    });
  }

  if (search) {
    filters.push({
      field: "name",
      operator: "ilike",
      value: "%" + search + "%",
    });
  }

  return {
    combinator: "AND",
    filters: filters,
  };
})();

Handling Array Inputs and Array Fields

When Input Value is an Array

If your input element returns an array (like a multi-select), use it directly:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "combinator": "AND",
  "filters": [
    {
      "field": "category_id",
      "operator": "in",
      "value": elements["categories_multiselect"].value
    }
  ]
}

When Database Field is an Array

Use array-specific operators (cs, cd, ov, any, all):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Array field contains all selected values
{
  "combinator": "AND",
  "filters": [
    {
      "field": "tags",
      "operator": "cs",
      "value": elements["tags_input"].value
    }
  ]
}

// Array field overlaps with selected values (has any in common)
{
  "combinator": "AND",
  "filters": [
    {
      "field": "categories",
      "operator": "ov",
      "value": elements["categories_select"].value
    }
  ]
}

Combining Array Input with Array Field

Filter where an array field contains values from an array input:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "combinator": "AND",
  "filters": [
    {
      "field": "required_skills",
      "operator": "cs",
      "value": Array.isArray(elements["skills_multiselect"].value)
        ? elements["skills_multiselect"].value
        : [elements["skills_multiselect"].value]
    }
  ]
}

Best Practices for Filtering

  1. Handle Empty Values: Always check if filter values exist before including them. For multiple optional filters, use a self-calling function pattern:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    (() => {
      const search = elements["search"].value;
      const filters = [];
    
      if (search) {
        filters.push({
          field: "name",
          operator: "ilike",
          value: "%" + search + "%",
        });
      }
    
      return {
        combinator: "AND",
        filters: filters,
      };
    })();
    

    This ensures PostgREST never receives null/undefined values in filter objects, which can cause errors.

  2. Use Optional Chaining: Protect against undefined values:

    1
    
    value: elements["table"].selected?.row?.id;
    
  3. Use Self-Calling Functions for Multiple Filters: PostgREST is sensitive to null/undefined values. Define constants and only add filters when values exist:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    (() => {
      const minPrice = elements["min_price"].value;
      const maxPrice = elements["max_price"].value;
      const filters = [];
    
      if (minPrice) {
        filters.push({ field: "price", operator: "gte", value: minPrice });
      }
      if (maxPrice) {
        filters.push({ field: "price", operator: "lte", value: maxPrice });
      }
    
      return { combinator: "AND", filters: filters };
    })();
    
  4. Use Appropriate Operators:

    • Use ilike for case-insensitive text search
    • Use in for matching multiple discrete values
    • Use cs/ov for array field operations
    • Use is with null for null checks
  5. Format Dates Properly: Always use ISO format for dates:

    1
    
    value: moment(elements["date"].value).toISOString();
    

Common Filter Patterns

Pattern 1: Search with Multiple Optional Filters

Use a self-calling function to safely handle multiple optional filters:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
(() => {
  const search = elements["search"].value;
  const status = elements["status"].value;
  const category = elements["category"].value;
  const filters = [];

  if (search) {
    filters.push({
      field: "name",
      operator: "ilike",
      value: "%" + search + "%",
    });
  }

  if (status) {
    filters.push({
      field: "status",
      operator: "eq",
      value: status,
    });
  }

  if (category) {
    filters.push({
      field: "category_id",
      operator: "eq",
      value: category,
    });
  }

  return {
    combinator: "AND",
    filters: filters,
  };
})();

Pattern 2: Date Range with Null Safety

Use a self-calling function for optional date ranges:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
(() => {
  const dateFrom = elements["date_from"].value;
  const dateTo = elements["date_to"].value;
  const filters = [];

  if (dateFrom) {
    filters.push({
      field: "created_at",
      operator: "gte",
      value: moment(dateFrom).toISOString(),
    });
  }

  if (dateTo) {
    filters.push({
      field: "created_at",
      operator: "lte",
      value: moment(dateTo).endOf("day").toISOString(),
    });
  }

  return {
    combinator: "AND",
    filters: filters,
  };
})();

Pattern 3: Array Field Filtering

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "combinator": "AND",
  "filters": [
    {
      field: "tags",
      operator: "cs",
      value: Array.isArray(elements["tags"].value)
        ? elements["tags"].value
        : elements["tags"].value ? [elements["tags"].value] : []
    }
  ]
}

Troubleshooting Filter Issues

  1. Filter Not Working: Check that the field name matches the database column exactly (case-sensitive)
  2. PostgREST Sensitivity: PostgREST is sensitive to null/undefined values in filters. Use self-calling functions to only include filters when values exist (see Best Practices section)
  3. Array Filter Issues: Ensure you’re using the correct operator (cs for contains, ov for overlap)
  4. Date Filter Issues: Always format dates as ISO strings using moment().toISOString()
  5. Null Values: Use the is operator with null to check for null values
  6. Empty Arrays: Check if array inputs are empty before using them in filters
  7. Operator Not Supported: If an operator doesn’t work, verify it’s supported in CYPEX. Refer to the operator tables above or check the PostgREST documentation for the complete list of available operators

Accessible JavaScript objects

Location

The first thing to understand is how to navigate inside the page. There are many variables which are of key importance. These can be used to figure out where we are and how to navigate through the application. Let’s inspect these variables in more detail to figure out how it all works, and what is possible:

location.pathname

pathname is a string which contains the URL’s path for the location, which will be an empty string if there is no path.

location.queries

This variable is a string containing a ‘?’ followed by the parameters of the URL. In CYPEX this is also an object containing arguments.

page

This object represents the current page of the application.

page.id

Identifier of the current page.

page.loadedAt

Date and time of the last page load.

elements

Elements is an object which contains all elements located on the current page. To access the element selector, the element identifier should be picked from the list, e.g., elements.<element_id>.

Here’s an example showing all element names on the page. Mind that there is tab-completion at work. Simple type “elements” and CYPEX will immediately display all variables inside the object:

elements

element

The interface of the element itself. It can be used in the custom expression field for the current scope of the element. For example: element.value

element.i18n

An object containing translated texts in the current language, e.g., title, label, etc. for this element.

props

The properties passed down by the parent element, for example, table or form, so their child elements will have access to props.data (= variable containing data elements).

lodash

A modern JavaScript utility library delivering modularity and performance can be used inside the “Custom Expression” editor.

Check out Lodash for more information.

Chart Filter as “Custom Expression“

Server data can be filtered by any element selector value.

Advanced filters might look like this:

{
  "combinator": "AND",
  "filters": []
}

It uses the selected table row value as a column to filter.

Syntax: elements.<table_id>.selected.row.<column_name>.

To create advanced filters, CYPEX uses PostgREST, so the filters array must contain a collection of possible combinations like:

{
    field: <column_name>,
    operator: "eq",
    value: elements.<table_id>.selected.row.<column_name>
 }

The following image contains a real-world example:

Filters

Values will be returned as strings.

The full list of PostgREST filters can be found here: https://postgrest.org/en/stable/api.html?highlight=operators#operators

JavaScript

A scripting or programming language, running inside the web-browser that allows you to implement complex display logic and features for websites.

The web reference for JavaScript: https://developer.mozilla.org/en-US/docs/Web/JavaScript

The following listing contains a little cheat sheet for your daily work:

Basics

Literal values
500 						number
"Star Wars"			string
true						boolean
[1, 2, 3]				array
{success: "green", error: "red"}	object
Expressions
100 (+ - * / ) 2.5			types of calculation
"Star " + "Wars"				string concatenation
true && || false				opperators
Inline conditionals
6 / 2 == 3
1 > 2 ? "success" : "error"
"Han Solo".endsWith("o")

You can use all modern JavaScript features available in your browser:

"Luke Skywalker".split(" ")
["Luke", "Leia"][0]
{success: "green", error: "red"}["green"]
Object.keys({foo: 1, bar: 2})
Modern JavaScript features:
  ({data:null}).data?.value
  null ?? "fallback"
  `${5 * 4} years old`
Expressions available as table child:
1. Access a specific field of the current row
  props.data["first_name"]
  props.data["created_at"]
  props.data["Name with non-alphanumeric characters!"]
2. Access all data

This can be used in a json field to visualize the whole row at once

  props.data
Accessing Own Element Data
  element.data
  element.value
Accessing Other Elements Data
  elements["some_markdown_field"].text
  elements["some_table"].data
Accessing Props
  data.props["name"]
Accessing Element Translations
  i18n.text
Accessing The Location Object
  location.query.identifier
Accessing The Page
  page.loadedAt

In one of the previous sections, you learned that there are actually two ways to resolve ID’s in a relational model: a.) use queries and joins or b.) fix things on the client side, using expressions. The beauty of expressions is that they’re usually far easier to handle, since you don’t have to touch the database at all. However, you also need to keep an eye on performance. Depending on your situation, one or the other might result in faster performance.

Displaying elements conditionally

All examples shown in this tutorial so far rely on the fact that elements have always been shown - regardless of the the situation on the page and the data displayed. In reality this isn’t always the case. Sometimes it’s necessary to show elements only in certain situations.

What are some examples of this? Suppose you only want to display an image in case some checkbox is ticked. Or maybe you want to display a button, but only when some fields are filled out. There are countless scenarios where you need conditional elements.

CYPEX supports the notion of a conditional container. What that means is that it’s possible to use a condition to display a group of elements which depend on that condition.

Hiding a button conditionally

Let’s see how it works and see how to hide a button. The goal is: the “Edit” button should only be visible if a value in a dropdown has been selected. If there’s no value, the button should be hidden.

The way to do that is by adding a “Conditional Container” element to the GUI. The elements you want to show / hide can then be added to this element. Then you need to assign a JavaScript expression to the element. In case it returns true, everything is visible:

Conditional Container

However, if this expression does not return true, but false, the elements in the container will be hidden from view. The advantage is that you can basically access all elements on the page and use those values to control this kind of behavior.

Before you take a look at the expression you need to put into the “Show content field”, you can see what the desired output looks like:

Conditional Container

No value has been selected and therefore the button is hidden. As soon as you select a value, the button will be displayed:

Conditional Container

Let’s take a look at the JavaScript expression you’ll need:

!!elements.default_autocomplete_input_2bedb141.value

This expression is sure to return true or false. But what does it actually mean? You can access all elements on the screen (“elements”). Every element on the page will automatically have a name. In this case CYPEX decided to call the element “default_autocomplete_input_2bedb141” (check the name of the element in the configuration window). Then you can access the value of this element. If it’s there it returns true - if it isn’t there, it returns false.

Almost any level of complexity is allowed here. All you have to do is to produce “true” or “false” to tell the container what to do.

List of element interfaces:

The following examples show how you can make use of variables, and access fields and information using the graphical editor.

Data Display

element. itself or elements.<data_dispaly_id>.

element.color

color: Access to element color, specified below.

element.data

Access to the element data if “Query name“ is set as a data source. Depending on the configuration mode, the data can be an object or an array.

For the mode “First Row“ is element.data an object.

For the mode “All Data“, element.data[ ] is an array, use element index access to specific record e.g.

element.error

Since this element fetches data by the “Query“ name, the error message is accessible if the request fails.

element.formattedData

Get the value of the preformatted data, e.g.,

formattedData

element.identifier

Returns identifier value if it’s set in the configuration.

element.identifier

element.loading

Boolean value. Indicates if the data is in the process of being loaded from the server.

i18n

This section describes internationalization and multi-language support.

i18n.label

Returns a string containing the current translated label.

translated label

i18n.text or elements.<markdown_text_id>.i18n.text

Translated texts in the current language, returned by markdown editor.

label

Pie / Bar / Line Chart

Let’s focus on configuration parameters available to control charts.

element or elements.<chart_id>.

control charts

element.data

An array of records, server data fetched by “Query “ name.

element.error

Returns the error message if the request fails.

element.loading

The boolean value indicates if the data is in the process of being loaded from the server.

element.selected

Returns selected object(record) if selection in chart configuration is enabled.

element.i18n.title

Returns a string that’s translated into the current language “Title“.

Table

Tables also support custom expressions. This section describes which features are available and what can be done to make this important GUI element more powerful.

element. or elements.<table_id>.

Table

element.data

Server data, fetch by setting the proper “Query Name“.

element.error

Returns an error message if the request fails.

element.limit

The number of rows per page.

element.loading

The boolean value indicates if the data is in the process of being loaded from the server.

element.loadingParams

A partisan object of the params set during data loading.

loadingParams

element.loadingParams.filter

An object of advanced table filters, if it exists.

element.loading Params.limit

The number of max rows can be fetched.

element.loadingParams.offset

The number of rows to skip before beginning to return rows.

element.loading.order

An optional array of objects like:

{
  fieldName: string;
  asc: boolean;
  hidden?: boolean;
}

element.metadata

metadata

element.metadata.canDelete

A boolean value, returns a value indicating if the user has sufficient permissions to delete records.

element.metadata.canUpdate

A boolean value, returns a value indicating if the user has sufficient permission to update records.

element.metadata.rows

Metadata related to each row / record where the property is a row key;

Metadata rows

element.metadata.rows[0].canDelete and canUpdate

Specify permission for the current row.

element.metadata.rows[0].currentStateI18n

Translation object generated on the server during table creation.

  .short_desc: string
  .title: string
  .long_desc: string
element.metadata.rows[0].stateChanges

An array of objects with possible workflows:

{
  to: string,     	// workflow value
  i18n: object  	 	// translation object, e.g., { title: string }
}
element.metadata.rows[0].stateName

Is a column name which contains workflow values.

element.nextFilter

This is an advanced option. An object of advanced table filters is a filter object which can be fetched while the user is about to build the filter. You will need this to quickly preview output.

element.NextPageAvailable

Is a boolean value that shows if the last page has already been fetched or not.

element.offset and element.order

Same as in loadingParams. The only difference is that these values can be configured in the element editor.

element.orderIndexed

Has the same definition as order, but is used as a helper for column sorting.

element.params

Also contains filter, limit, offset, and the order specified in the URL to fetch table data.

element.references

An object of joined tables (referenced table), if such tables exist (they do exist in case you use “default resolution” in the model builder). This configuration can be found in the “References“ section of the table editor:

references

Each property or value is accessible in the “Custom Expression“ editor. The property name is a referenced column name (see image)

references

and the value is an object: .viewName: string (viewName: is the name of a joined table) .identifierName: string (identifierName: is the joined table identifier)

element.searchInputValue

Access to the value of the table search input:

searchInputValue

Example: element.searchInputValue = "Dell"

element.selected

For tables with “Selection“ enabled selected value can be used as an expression:

selected

To get data of the whole table row, even to columns that aren’t displayed in the table but natively present in the “Query“, the following syntax should be used:

elements.<table_id>.selected.row.<column_name>

or for usage inside the element itself

element.selected.row.<column_name>

For example, to use the column “Name“ as a title for another element, which uses the “Custom Expression“ editor:

column_name

**Note: **If “First row selected“ isn’t enabled and the row wasn’t clicked, it means the selected object is empty. In this situation, use “?”

which is a JavaScript operator to avoid errors if no value exists:

elements.<table_id>.selected?.row?.<column_name> ?? "Default Value"

“Default Value“ can also be an empty string.

Table columns

In the current version of CYPEX, table columns are elements but without external access. It’s impossible to get a column value inside the “Custom Expression“ editor used by any other element on the page. However, every column type has access to the table data through the props key.

Here’s an example:

Table columns

To get the data of a column inside the props objects, use the following syntax: props.data["remote"]. In this case you access the column called “remote” and fetch the idea. The Boolean() method will ensure that the value is neither null nor undefined.

Use “Custom expressions” to format the string or to adjust the output according to your needs. It’s also possible to pick the necessary column using autocomplete inside the “Text Field“, if adjustments aren’t required.

Table columns

Props object

The props object is one of the most fundamental building blocks of the “Custom expressions” machinery. It contains all object-related data, keys, metadata as well as references. It’s the single most important object you must understand when working with CYPEX expressions in general:

props.metadata

props.data

Row data object.

props.key

Row index.

props.metadata

Row metadata passed through from the table element

Props object

props.metadta.canDelete & props.metadata.canUpdate

Are boolean values defining whether the user has permission to delete or update?

props.references

Only the parent table has any of the references(joined tables) configured; it’s possible to get those references using the following method:

props.references

props.references.id

Specifies the source column in this example.

Keep in mind the whole referenced row will be returned as a value. Use autocomplete to select the desired field or use a JavaScript expression to access various fields as needed:

props.references

To get any value from the referenced row, just pick the desired column names.

Form

Let’s focus our attention on forms which need special infrastructure to work properly. The following variables exist in this context.

element. or elements.<form_id>

Form

element.data

Server data, fetch by setting the proper “Query Name“.

Note: The Form data object also has access to values referenced, if the form has any joined (referenced) queries.

element.data

An example: To get the value of “manufacturers“, use the following syntax:

element.<form_id>.data.manufacturers

In this example, the manufacturer’s column does not exist in a query that belongs to the form, but this value was joined by configuring “Form“ references.

element.errors

An object of possible server errors, available only if errors exist.

element.hasChanges

Is a boolean value. The value is “true” in case the form has been changed.

element.identifier

String or number required for identifying a record in the form with type “Edit“ or “Detail“.

element.isValid

Is a boolean value that describes if the form is valid or not.

element.loadState and elements.saveState

These are both objects which look as follows:

{
  inProgress: boolean;
  error: string | { message: string }
}

element.inProgress

Shows whether save or load action is in process. It contains “error” in case the request is failing or has failed. An error message is provided.

element.originalData

Initially fetched data. The original copy of the data is preserved until the form is submitted, so that you can always ensure that the changes can be reverted back to what was stored before.

element.touched

Boolean value shows if the user has touched any form input.

Conditional Container

element. or elements.<conditional_container_id>

Conditional Container

element.visible

Boolean value. Since “Conditional Containers“ serve to display elements conditionally, depending on whether “visible” is set to “true” or “false”. In CYPEX the visibility of an element on a page can be turned on and off.

Note: To toggle an element’s visibility inside a “Conditional Container“ use the configuration value of other components such as “Boolean Input“ elements on the same page.

elements.<boolean_input_id>.value

Tabs

element. or elements.<tabs_id>

Tabs

element.indexSelected

Returns a number (index) of the active tab.

Inputs

All inputs in CYPEX are accessible, like elements. or elements.<input_id>

element.value

Returns a value depending on the input type, e.g., “Number Input“ has an integer value, “Text Input“ a string, and so on.

element.disabled

A boolean value indicating whether the input is read-only or not.

element.touched

Shows if the user interacted with the current input.

element.errors

Optional key. Contains form data validation errors.

Note: Controlled Inputs

All inputs inside the form are controlled by the form they belong to.

The “Data Source” section is the place to go to get the data from the parent form.

For example:

Controlled Inputs

Where “Element Id“ is a parent form element ID. “Field Path“ wanted the column to be displayed.

So, the default value of the controlled input is “Form” data passed through the input props.

Autocomplete Input

Autocomplete Input

element.loadingOptions

Is a boolean value that shows if option fetching is in process.

element.options

Is an array of options, each option is an object. Here’s an example:

{
  value: string | number;
  label: string;
}

element.optionsError

Server error if fetching options fails.

element.rawOptions

An array. The row data fetched if “Options Source“ is a query.

element.rawValueObject

An object. The data containing the selected row.

element.searchInputValue

A string. The user input holding the value you are searching for.

element.valueObject

If value is selected, the value of the object is simliar to how it is in the the following example:

{ value: string | number; label: string } is accessible.

File Input & Multiple File Input

File Input & Multiple File Input

element.file

Access to uploaded files.

element.loading

Is a boolean value that shows if the file is currently uploading.

element.metadata

An object, uploaded file metadata:

{
  "hash": string;
  "fileName": string;
  "realName": string;
  "fileType": string; // e.g., "image/png"
  "fileGroup": {
  "id": string;
  "name": string;
    // e.g., "public" | "private",
  "acl": string[];
    // array of strings (roles),
    // permissions for file group, e.g., ["cypex_admin"]
  },
  "typeGroup": {
    "id": string;
    "typeName": string; // e.g., "image"
  },
  "acl": string[]; // array of strings (roles), permissions for file, e.g., ["cypex_admin"],
  "id": string;
}

element.metadataError

A string error message.

element.uploadError

This value is a string error message, defined in case uploading fails.

element.uploadError

element.files

Access to an array of uploaded files.

element.metadata

Same as for the single file, but an array of metadata objects.

Subform table

Subform tables serve mostly as form input, in the case of “References“ are configured (at least one). So “Subform table“ can update joined tables, used for 1:n relations during data editing. Subforms used as input have the same input properties mentioned above, but the value is an array of objects (joined table data). The syntax to get those values is as follows: elements.<sub_form_table_id>.value returns Array<object>.

Fields

Various elements in CYPEX are accessible as shown in the following listing:

element. or elements.<input_id>

Google Maps

In this section you’ll learn how custom expressions can help to make maps in CYPEX better.

element.data

An array of markers, markers are objects:

{
  lat: number; // latitude
  lng: number; // longitude
  name: string;
}

element.loading

Is a boolean value indicating if data is loading or not.

element.error

Error message in case data load fails.

element.selected

Returns marker (check the type above) object, only if any is selected by the user.

Action Button

alt_text

element.clickedCount

Number of times the button was clicked.

element.lastClicked

A date type, last time the button was clicked.

Call Button

Call Button

element.error

An error message in case the function call fails.

element.loading

A boolean value. Indicates if the function is being called right now.

element.result

The result of a function call.

Internal Link Field

element.data

In order to have access to data objects a “Data Source“ is required. In this case, data will be a record (table row). You have access to the data as well as to identifiers and status-related information.

element.error

Error message in case a request fails.

element.hasStarted

A boolean value, indicates if data has started loading.

element.identifier

String or number, in case the identifier was set in the “Data Source“ section.

element.loading

Boolean value, if loading currently in process.

Number Field

value: a number, field value.