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.
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:
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:
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.
The CYPEX development team wants to provide our customers with the most flexible solution possible. We’ve visualized abstract tools to make them as easy to use as possible. However, many applications need more than just display elements which put a 1:1 copy of data on the screen. To make a truly beautiful application, it’s necessary to add format options, dependencies and a lot more.
The solution to the problem of providing end users with a powerful and easy-to-use GUI is the introduction of “custom expressions”. In the GUI, most elements can be fine-tuned by using custom JavaScript code. Why is that necessary? Here are some examples:
- Hiding or showing elements depending on a value in a data source
- Applying colors which depend on the content of a variable
- Calculating values on the fly
There are many more examples proving why expressions make sense.
In this section, we’ll take a look at custom expressions and understand how they can be used.
In CYPEX, each element on the page has access to its selectors. So what are selectors? Let’s dive in and find out. Selectors are predefined JavaScript objects with properties and values. CYPEX uses selectors to make elements on the page interact with each other in a controlled way.
Many GUI elements allow for custom expressions. The configuration editors provide a way to define “Custom Expressions” as input. Depending on the element configuration, this input should return a value, e.g., string, number, array, object, or function. We have documented the required value for each element to make it easier for developers to adjust the configuration. We recommend checking out our video tutorial series.
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:
pathname
is a string which contains the URL’s path for the location, which will be an empty string if there is no path.
This variable is a string containing a ‘?’ followed by the parameters of the URL. In CYPEX this is also an object containing arguments.
This object represents the current page of the application.
Identifier of the current page.
Date and time of the last page load.
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:
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
An object containing translated texts in the current language, e.g., title, label, etc. for this element.
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).
A modern JavaScript utility library delivering modularity and performance can be used inside the “Custom Expression” editor.
Check out Lodash for more information.
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:
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
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:
500 number
"Star Wars" string
true boolean
[1, 2, 3] array
{success: "green", error: "red"} object
100 (+ - * / ) 2.5 types of calculation
"Star " + "Wars" string concatenation
true && || false opperators
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})
({data:null}).data?.value
null ?? "fallback"
`${5 * 4} years old`
props.data["first_name"]
props.data["created_at"]
props.data["Name with non-alphanumeric characters!"]
This can be used in a json field to visualize the whole row at once
props.data
element.data
element.value
elements["some_markdown_field"].text
elements["some_table"].data
data.props["name"]
i18n.text
location.query.identifier
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.
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.
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:
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:
No value has been selected and therefore the button is hidden. As soon as you select a value, the button will be displayed:
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.
The following examples show how you can make use of variables, and access fields and information using the graphical editor.
element
. itself or elements.<data_dispaly_id>
.
color
: Access to element color, specified below.
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.
Since this element fetches data by the “Query“ name, the error message is accessible if the request fails.
Get the value of the preformatted data, e.g.,
Returns identifier value if it’s set in the configuration.
element.loading
Boolean value. Indicates if the data is in the process of being loaded from the server.
This section describes internationalization and multi-language support.
Returns a string containing the current translated label.
i18n.text or elements.<markdown_text_id>.i18n.text
Translated texts in the current language, returned by markdown editor.
Let’s focus on configuration parameters available to control charts.
element
or elements.<chart_id>
.
An array of records, server data fetched by “Query “ name.
Returns the error message if the request fails.
The boolean value indicates if the data is in the process of being loaded from the server.
Returns selected object(record) if selection in chart configuration is enabled.
Returns a string that’s translated into the current language “Title“.
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>
.
Server data, fetch by setting the proper “Query Name“.
Returns an error message if the request fails.
The number of rows per page.
The boolean value indicates if the data is in the process of being loaded from the server.
A partisan object of the params set during data loading.
An object of advanced table filters, if it exists.
The number of max rows can be fetched.
The number of rows to skip before beginning to return rows.
An optional array of objects like:
{
fieldName: string;
asc: boolean;
hidden?: boolean;
}
A boolean value, returns a value indicating if the user has sufficient permissions to delete records.
A boolean value, returns a value indicating if the user has sufficient permission to update records.
Metadata related to each row / record where the property is a row key;
element.metadata.rows[0].canDelete and canUpdate
Specify permission for the current row.
Translation object generated on the server during table creation.
.short_desc: string
.title: string
.long_desc: string
An array of objects with possible workflows:
{
to: string, // workflow value
i18n: object // translation object, e.g., { title: string }
}
Is a column name which contains workflow values.
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.
Is a boolean value that shows if the last page has already been fetched or not.
Same as in loadingParams.
The only difference is that these values can be configured in the element editor.
Has the same definition as order,
but is used as a helper for column sorting.
Also contains filter, limit, offset
, and the order specified in the URL to fetch table data.
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:
Each property or value is accessible in the “Custom Expression“ editor. The property name is a referenced column name (see image)
and the value is an object: .viewName: string
(viewName: is the name of a joined table) .identifierName: string
(identifierName: is the joined table identifier)
Access to the value of the table search input:
Example: element.searchInputValue = "Dell"
For tables with “Selection“ enabled selected value can be used as an expression:
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:
**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.
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:
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.
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:
Row data object.
Row index.
Row metadata passed through from the table element
Are boolean values defining whether the user has permission to delete or update?
Only the parent table has any of the references(joined tables) configured; it’s possible to get those references using the following method:
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:
To get any value from the referenced row, just pick the desired column names.
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>
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.
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.
An object of possible server errors, available only if errors exist.
Is a boolean value. The value is “true
” in case the form has been changed.
String or number required for identifying a record in the form with type “Edit“ or “Detail“.
Is a boolean value that describes if the form is valid or not.
These are both objects which look as follows:
{
inProgress: boolean;
error: string | { message: string }
}
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.
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.
Boolean value shows if the user has touched any form input.
element. or elements.<conditional_container_id>
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
element. or elements.<tabs_id>
Returns a number (index) of the active tab.
All inputs in CYPEX are accessible, like elements. or elements.<input_id>
Returns a value depending on the input type, e.g., “Number Input“ has an integer value, “Text Input“ a string, and so on.
A boolean value indicating whether the input is read-only or not.
Shows if the user interacted with the current input.
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:
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.
Is a boolean value that shows if option fetching is in process.
Is an array of options, each option is an object. Here’s an example:
{
value: string | number;
label: string;
}
Server error if fetching options fails.
An array. The row data fetched if “Options Source“ is a query.
An object. The data containing the selected row.
A string. The user input holding the value you are searching for.
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.
Access to uploaded files.
Is a boolean value that shows if the file is currently uploading.
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;
}
A string error message.
This value is a string error message, defined in case uploading fails.
Access to an array of uploaded files.
Same as for the single file, but an array of metadata objects.
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>
.
Various elements in CYPEX are accessible as shown in the following listing:
element. or elements.<input_id>
In this section you’ll learn how custom expressions can help to make maps in CYPEX better.
An array of markers, markers are objects:
{
lat: number; // latitude
lng: number; // longitude
name: string;
}
Is a boolean value indicating if data is loading or not.
Error message in case data load fails.
Returns marker (check the type above) object, only if any is selected by the user.
Number of times the button was clicked.
A date type, last time the button was clicked.
An error message in case the function call fails.
A boolean value. Indicates if the function is being called right now.
The result of a function call.
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.
Error message in case a request fails.
A boolean value, indicates if data has started loading.
String or number, in case the identifier was set in the “Data Source“ section.
Boolean value, if loading currently in process.
value: a number, field value.