The KoiLabel component is designed to display a value in the interface, either in its original form or after modification.
There may be multiple values. A value does not necessarily have to be of type string. A value can be directly retrieved from the data or constructed based on it.
For instance, in the example above, the component displays a mathematical expression. The component's internal data contains two integer values: one equals 2, and the other equals 3. The result of their multiplication is 6, and the final expression, "2 * 3 = 6," represents the constructed value.
Values can be generated from the component's internal data or derived from the data of other components.
Class
The initialization process of the KoiLabel component is as follows:
-
_onConstructed() KoiDataCapable
- _constructState() KoiStateCapable
- _constructData() KoiStringDataCapable
- _constructSocket() KoiLabelSocketConnectable
-
_onBeforeConnected() KoiDataCapable
- _prepareDefaultDataValuesFromAttributes() KoiDataCapable
- _prepareSocket() KoiSocketConnectable
-
- socket.getTemplate() KoiLabelSocket
-
_onConnected() KoiElementStencil
- _updateSomethingWhenConnected() KoiElementStencil
-
- _updateOwnDataWhenConnected() KoiElementStencil
- _updateStateCodeWhenConnected() KoiStateCapable
-
_onAfterConnected() KoiElementStencil
- isSomethingChanged() KoiDataCapable
- _handleSomethingChangedWhenAfterConnected() KoiBaseControl
-
- updateAppearance() KoiBaseControl
The KoiLabel component is a subclass of KoiLabelStencil and implements the KoiStringDataCapable and KoiLabelSocketConnectable behaviors.
The KoiLabelStencil class, which extends KoiBaseControl, uses the KoiSocketConnectable behavior to manage the output of information to the component’s socket. This information is displayed not only during the component's initialization but also whenever changes occur in the data source.
The KoiStringDataCapable behavior enables the component to use its internal data of type KoiStringData as a data source.
The KoiLabelSocketConnectable behavior provides the component with a KoiLabelSocket, which facilitates the display of string values within the socket.
The KoiLabelSocket class determines which component will render the value by defining the getTemplate method. By default, KoiLabelSocket uses a standard span element as the connected component.
The _updateAppearance method is central to the KoiLabel component. It handles both the initialization process and the updates triggered by changes in the component’s internal data.
- isStateAbnormal() KoiStateCapable
- _beforeDisplayNormalState() KoiSocketConnectable
-
- _displaySocket() KoiSocketConnectable
- _displayNormalState() KoiSocketConnectable
-
- _updateSocket() KoiLabelStencil
-
- _getTextToDisplayInSocket() KoiLabelStencil
-
- _getDataToDisplayInSocket() KoiLabel
- _convertDataToText() KoiLabel
- _updateTextInSocket() KoiLabelSocketConnectable
-
- socket.displayText() KoiLabelSocket
The _updateAppearance method calls _updateSocket, which is implemented in the KoiLabelStencil class. This method retrieves data, transforms it into a textual representation, and outputs it to the socket. Consequently, _updateSocket establishes the fundamental functionality of KoiLabel.
The _getDataToDisplayInSocket and _convertDataToText methods are defined in the KoiLabel class and specify the source of the data and the logic for converting it into text. By overriding these methods and combining the component with alternative DataCapable and SocketConnectable behaviors, various types of KoiLabel components can be created. For example, the previously mentioned DocsMultiplyLabel is one such variant.
Data
The internal data of the KoiLabel component is of type KoiStringData and follows a defined schema.
The KoiDataValueChangeable behavior provides two key methods:
- getValue: Retrieves the current value of the internal data.
- setValue: Assigns a new value to the internal data.
The setValue method is used by the KoiLabel component as part of its implementation of the KoiStringDataCapable behavior. This behavior introduces the public method attemptChangeValue, which updates the internal data value. Once the internal data changes, the component's displayed value is updated automatically.
The getValue method is utilized in the _convertDataToText method. This method determines how the internal data value is transformed into the format displayed by the component.
Attributes
id | The unique identifier for the component. |
---|---|
value | The initial value displayed by the component. |
The value attribute is used to set the default internal data value for the component. Its properties are as follows:
- It specifies the value displayed by default.
- Modifying this attribute after the component has been created does not update the displayed value.
State
The KoiLabel component can be toggled between hidden and visible states using the hide and show methods provided by its base class, KoiBaseControl.
In addition, the KoiLabel component can respond to changes in the state of its internal data.
For instance, if the initial value in the tag attributes is not set, the component should display an error. To achieve this, the component must validate its internal data during initialization. Furthermore, the internal data should treat the absence of a value as an error.
While components automatically validate data during data modification attempts, retrieving default values from attributes is not considered a modification. To ensure data validation during initialization — after the attributes' values have been retrieved — you should extend the _updateOwnDataWhenConnected method. Within this method, access the data element and invoke its validateValueAndMarkValidity method.
To ensure the data element treats the absence of a value as an error, set its allow_empty property to false.
As a result, during the _updateSomethingWhenConnected phase, the component will validate the data, update its state based on the validation outcome, and display the state using the displayError method.
In this example, no initial value was provided for the component's internal data. This resulted in the absence of a value in the internal data and subsequently triggered the displayError method.
Event
The KoiLabel component manages its own internal data but does not emit any events when the internal data value changes.
To make the KoiLabel component dispatch the koi-changed event, you need to create a subclass that implements the KoiChangedEventDispatchable behavior. An example of such a component is provided in the Automatic Updates section below.
Methods
attemptChangeValue(new_value) | Modifies the component's internal data and updates the displayed value accordingly. |
---|
The KoiStringDataCapable behavior provides the attemptChangeValue method to handle both internal data updates and corresponding visual changes.
- data.setValue() KoiDataValueChangeable
- _updateSomethingWhenChanged() KoiDataCapable
-
- _updateOwnDataWhenChanged() KoiDataCapable
- _updateStateCodeWhenChanged() KoiDataCapable
- _onAfterChanged() KoiElementStencil
-
- isSomethingChanged() KoiDataCapable
- _handleSomethingChanged() KoiBaseControl
-
- _updateAppearance() KoiBaseControl
- _setNothingChanged() KoiDataCapable
This method modifies the value of the internal data and subsequently calls the _updateSomethingWhenChanged method, which updates the component’s state to align with the new internal data.
Afterward, the _onAfterChanged method is invoked, allowing the component to respond to the data change. This includes updating the visual representation in the socket through the _updateAppearance method.
In the example provided, the label dynamically updates when buttons are clicked. The change is achieved by calling the attemptChangeValue(new_value) method.
Thanks to its ability to display an initial value during initialization and update it later, the KoiLabel component can be used in several configurations.
- KoiLabel can be rendered in the getTemplate method of the external component and the value attribute can be set immediately. This is a good approach when the label does not depend on any data from the external component or other components.
- KoiLabel can be rendered in the _beforeDisplayNormalState method of the external component and the value attribute can be set immediately. This is a good approach when the label depends on the state and data of the external component, but does not change thereafter.
- KoiLabel can be rendered in the _beforeDisplayNormalState method of the external component and the initial value of the value attribute can be set. Then, in the _displayNormalState method of the external component, this value can be modified by calling attemptChangeValue(new_value). In this way, the label will respond to changes in the external component's data.
- KoiLabel can be rendered in the _displayNormalState method of the external component and the initial value of the value attribute can be set. This way, the external component can dynamically create and delete labels.
Here is an example where labels are automatically created and deleted, reflecting the data of the external component.
In the above example, the composite component in the _applyOperationToSocket handler adds and removes labels depending on which button is pressed. The composite component determines the default value for the label based on how many labels it contains.
Automatic Updates
The value passed to the component via the attribute has two characteristics that limit its applicability. First, it only sets the initial value, and to modify it, the attemptChangeValue method must be called, which requires a controller to manage the label. Second, when the attribute value contains special characters, such as quotes, it may not be processed correctly by the browser, and escaping is needed.
However, there is another way to use labels – making them track and display changes in external data by themselves.
If the label displays external data, its own data is usually not needed. In this case, the KoiLabel component is no longer necessary, since it uses its own data. We can create a KoiConnectedLabel component, which is fully dependent on the state and data of the external component.
We will inherit the KoiConnectedLabel component from KoiLabelStencil, as with KoiLabel, but we will not implement the KoiStringDataCapable behavior for it. Instead, we will consider the KoiControlConnectorInteractable behavior.
This behavior allows the component to respond to changes in the connector's data: updating the state and display, and receiving data from the connector.
However, if the KoiLabel component implements the KoiStringDataCapable behavior, and because of this knows that it can retrieve its own data value using the getValue method of the KoiStringData class, the component connected to the connector does not know what kind of data the connector is passing to it.
For the KoiConnectedLabel component to work with data coming from the connector, it implements not the KoiControlConnectorInteractable behavior, but the inherited KoiControlStringDataConnectorInteractable behavior, which allows it to connect to data providers whose own data is of type KoiStringData.
In principle, by implementing a behavior whose name indicates the type of data object used, we give the component full permission to directly access the data objects received from the connector. That is, since the KoiConnectedLabel component specifies the KoiControlStringDataConnectorInteractable behavior, the component can retrieve the value of the data object using the data.getValue method.
However, this practice slightly disrupts the code, scattering method calls for the data object throughout the component's code. Ideally, the methods should be gathered in one place and represent something akin to an interface that describes what exactly the component can do with the connector's data object. To achieve this, the KoiControlStringDataConnectorInteractable behavior provides the KoiConnectedLabel component with the _getValueFromConnectorData method, which the component uses to retrieve the data value and convert it to text sent to the socket.
In the example above, the label on the left is a KoiLabel, whose value is controlled by buttons, while the label on the right implements the KoiControlStringDataConnectorInteractable behavior, meaning it has a connector linked to the label on the left.
A standard KoiLabel component does not trigger events. For the label on the left to trigger events, it additionally implements the KoiChangedEventDispatchable behavior.
Each time the data of the label on the left changes, it generates a koi-changed event. The label on the right, using the connector, subscribes to this event and updates the displayed value using the data passed in the event.
The label on the right does not have its own data, so it can only update its display when the state changes. To change its state when the connector's data changes, it extends the _updateStateCodeWhenConnectorDataChanged method.
The KoiControlStringDataConnectorInteractable behavior assumes that the component has a provider_id attribute, pointing to the data source.
In this use case, the DocsConnectedLabel component becomes dependent on the state of the data source. So, if the source is not ready to provide data, the dependent component will call the displayAbnormalState method. You can see this in the example above, where if you set the state code "loading" for the left label, it indicates that the component is loading and cannot provide data.
Recommendations
The choice of the update method for labels depends on the complexity of the logic in the composite component that contains the labels.
If the label is part of a composite component, then the composite component is a "has-a" composition type, meaning it has access to the methods of all its internal components as if they were its own methods. That is, the composite component can call the methods of its internal components directly. Therefore, the composite component can manage the labels nested inside it.
Thus, the composite component acts as a controller. The controller tracks events and determines how the displayed label values should change.
In many cases, it is convenient to define once which label is responsible for what and not implement the logic for changing the displayed values in the controller, but instead bind each label to the appropriate provider directly via the connector.
On one hand, this approach is convenient and reduces the amount of logic in the controller. Additionally, it allows you to determine, without reading the controller code, which data and under what conditions a label displays, simply by looking at the label itself.
On the other hand, with this approach, the controller's logic no longer defines the behavior of the labels. That is, it becomes impossible to determine, by looking at the code that describes the controller's logic, which labels change and under what conditions when the data changes.
Therefore, I recommend placing the code that implements the controller's functions directly next to the code that defines the binding of labels to data.