Intent driven components

It’s undeniable that components play a significant role in intent-driven development. The variety of components mirrors the diversity of custom scenarios. While standard components are useful, many situations call for the creation of reusable UIs with custom logic and interface.

This article primarily explores the creation of custom web components from a JavaScript standpoint. However, our focus will be on how to develop these components within the framework of intent-driven development.

There are two schemas to consider.

  1. UI Schema
  2. Process Schema

The UI schema will generate the UI markup.
Most settings and configurations for the component should be done via attributes. For the most part this will take the affect of data- attributes.

Occasionally, it’s necessary to feed data into a component via properties, which can be accomplished using binding expressions. However, this approach can sometimes lead to timing issues, particularly when properties are assigned to the component before it’s prepared to process them. While this issue can be addressed within the component, a more efficient solution is to implement manageable states that activate specific activities, thus avoiding timing conflicts.

There are two main states to take note of.

  1. loaded – the component is busy initializing, it is attached and is ready receive data but it is not ready for use yet.
  2. ready – the component is ready for use. It has been initialized, the data has been loaded and initial rendering is complete.

The process api has a components module you can use in the “component” to notify the states.

  1. notify_loading – set the component’s data-loading attribute to true and dispatch an event “loading“.
  2. notify_ready – remove the data-loading attribute and set the data-ready attribute to true and dispatch an event “ready“.
await crs.call("component", "notify_loading", { element: this });
await crs.call("component", "notify_ready", { element: this });

Moreover, you can monitor these events through the process API. Utilize the ‘on_loading‘ and ‘on_ready‘ actions to track the aforementioned events and execute specific actions when they are triggered.

await crs.call("component", "on_loading", {
    "element": "#my-element", 
    "callback": () => { ... },
    "caller": this

Both ‘on_loading’ and ‘on_ready’ function similarly, as demonstrated above, with the only distinction being their action names. Each of these actions involves three parameters.

  1. element – Refers to either the CSS selector or the actual element to be monitored.
  2. callback – A function pointer that is invoked when the event occurs.
  3. caller – The object, if any, that should be referenced as “this” within the callback function.

In both listeners, if the ‘data-‘ attribute is already set, the callback will be triggered immediately. Otherwise, it will await the event’s dispatch before firing the callback.

The component’s functionalities should be accessible through the process API. Consider a data grid as an example. When adding or removing columns in the data grid component, direct interactions with the component instance should not be expected. Instead, these functionalities should be made available via an actions file, which acts as a process API module. Utilizing the standard structure of process API calls, updates to the component can be made. This principle applies to retrieving data, updating data, and executing function calls.

await crs.call("data_grid", "add_column", {
    "element": "#grdMain",
    "columns": [ ... column data ... ]

In the aforementioned example, one of the attributes passed to the process API is the ‘element,’ which could be a CSS selector or the actual component instance itself. The other args define the intent parameters required by the action being called.

Incorporating interactions into the process API provides a consistent method for interacting with the component. This uniformity benefits not only developers who may be writing code to modify the component but also those working with the schema. As all process API calls can be executed via the process API schema, it enables automated systems to carry out the same tasks. This approach ensures consistent behavior, regardless of who is initiating the calls or from where.

When developing web components, and considering “element-name” as the custom name of the component, the necessary files would include:

  1. element-name.html – component markup
  2. element-name.css – component styling
  3. element-name.js – component code
  4. element-name-actions.js – process api code

Generally, the UI schema necessitates no extra effort since it already accommodates standard HTML markup definitions as a convention. However, if the component is intricate and necessitates defining a specific usability data structure for ease in generating the component according to behavioral requirements, then creating a custom provider to accommodate this becomes essential.

In summary, the design of a component API should be approached with intent-driven development in mind, ensuring that it is reusable and accessible via a well-defined, modular API. This should be achieved using both the UI schema and process schema for execution control.

For more detailed component information please see the Bindable Element documentation.