Show Sidebar Menu

KoiCom Data Management with KoiProvider and KoiTransmitter

This article explains the KoiProvider component, a data storage tool in the KoiCom library. It manages data, triggers change events, and connects with other components. It also covers avoiding feedback loops and optimizing updates when multiple components interact, introducing KoiTransmitter for handling modified data separately.

Component for Storing Data.

Provider

The KoiProvider component is a non-visual component. In the example above, it is visible only because the corresponding CSS classes are applied to its tag.

The purpose of KoiProvider is to store data in its own data object. The data can be of any type. In the example above, the data type is KoiStringData, and thus the initial value of the data is defined by the value attribute.

Most often, the KoiProvider component acts as a data provider, meaning that other components connect to it through a connector. In the example above, the KoiConnectedLabel component is connected to the provider.

The ability to not only store data but also trigger events related to its readiness and changes, as well as to present the data object as an independent component, thereby encapsulating the handling of the data within the component, makes KoiProvider a convenient tool within the KoiCom library.

Class

The component initialization process looks as follows:

  1. _onConstructed() KoiDataCapable
    • _constructState() KoiStateCapable
    • _constructData() KoiDataCapable
  2. _onBeforeConnected() KoiDataCapable
    • _constructChangedEvent() KoiChangedEventDispatchable
    • _prepareDefaultDataValuesFromAttributes() KoiDataCapable
  3. _onConnected() KoiElementStencil
    • _updateSomethingWhenConnected() KoiElementStencil
      • _updateOwnDataWhenConnected() KoiElementStencil
      • _updateStateCodeWhenConnected() KoiStateCapable
  4. _onAfterConnected() KoiElementStencil
    • isSomethingChanged() KoiDataCapable
    • _handleSomethingChangedWhenAfterConnected() KoiChangedEventDispatchable
      • _dispatchChangedEventWhenAfterConnected() KoiChangedEventDispatchable
        • _dispatchChangedEvent() KoiChangedEventDispatchable
    • _setNothingChanged() KoiDataCapable
    • _subscribeToEvents() KoiElementStencil

The KoiProvider component is a subclass of KoiBaseElement and implements the behaviors KoiDataCapable and KoiChangedEventDispatchable.

The KoiDataCapable behavior defines that the component has its own data but does not define the data type. To determine the data type, the final component must implement the method _constructData.

The KoiChangedEventDispatchable behavior allows the component to serve as a data source, meaning it implements the process of triggering the koi-changed event when the data or state of the component changes.

Data

The KoiProvider component has its own data but does not restrict the data type. Subclasses of KoiProvider should determine what type of data they will store.

Attributes

id The component identifier

The KoiProvider component has only one attribute — the component identifier. Depending on the chosen data type, the number of attributes may increase.

Component State

The component does not have visible states but has a state defined by the KoiStateCapable behavior. The state code is determined based on the initialization stage and the state of its own data.

At the beginning of the initialization process, the KoiProvider component has the initializing state and cannot provide its own data, which can be easily checked by calling the isReady() method.

At the _onConnected stage, the component receives the ready state, and at the _onAfterConnected stage, it triggers the koi-changed event to indicate its readiness to provide data.

Event

The KoiProvider component has its own data and implements the KoiChangedEventDispatchable behavior, so it triggers the koi-changed event right after initialization and whenever its own data changes.

The koi-changed event object is of type CustomEvent and is created during the onBeforeConnected stage in the _constructChangedEvent method to avoid creating the object each time the event is generated.

Another component can use KoiProvider as a data source if it subscribes to this event. To do so, it must implement either the KoiSingleConnectorInitializable or KoiSingleConnectorInteractable behavior. In the first case, the component will be able to request data at the time of its own initialization but will not receive data if the KoiProvider is not yet initialized. In the second case, the component will also be able to request data at initialization but will be able to receive data from KoiProvider later, reacting to the koi-changed event.

Let's consider the KoiSingleConnectorInitializable behavior. Suppose a subclass of the KoiLabel component implements this behavior and initializes first.

Provider

As seen, if the data-receiving component initializes before the provider’s data is ready, it will not be able to get the data and will remain in the initializing state permanently.

Now suppose the subclass of KoiLabel initializes second.

Provider

In this case, the data-receiving component initializes successfully and receives data from the provider.

The difference in behavior between the first and second cases might seem unpredictable, but when the provider creates the data receiver internally, it is always initialized first. This is a common case, which can be seen in the code of the KoiDialogString component.

Another way to avoid ambiguity is by using the KoiSingleConnectorInteractable behavior. Suppose a subclass of the KoiLabel component implements this behavior and initializes first.

Provider

As seen, the data receiver is initially incompletely initialized, but by intercepting the koi-changed event, it eventually receives data from the provider. Now suppose the subclass of KoiLabel initializes second.

Provider

As seen, in this case, initialization proceeds successfully as well.

It is important to consider these methods for connecting a data receiver to the provider when using the KoiProvider component.

Application

As mentioned earlier, the KoiProvider component, with its ability to trigger events, is not just a data storage component but an active one that can transform data and participate in the data flow between components.

However, caution must be exercised when creating the data flow. Suppose an input field is connected to the provider, and upon the change event of this field, a controller modifies the data stored in the provider. The provider triggers the koi-changed event, the input field responds to this event, and triggers its own event, which the controller reacts to. This creates a feedback loop, which can only be resolved by the provider itself. The provider should not trigger a change event if its current data is equivalent to the data coming from the controller.

Let’s consider another aspect of the provider as a data supplier. Suppose the provider contains a list, and several components are subscribed to it, each displaying one row of the list. The components can modify their respective rows of the list, and the modified list needs to be returned.

Ideally, modifying one row of the list should only modify that specific row in the provider. However, any data change in the provider, even changing a single row, will trigger a response from all these components. Not only does the data flow need to be prevented from cycling, but it also needs to determine which row was modified based on the events coming from the components.

The responsibility for determining which row should be modified could be placed on the provider. However, the provider should not know which component needs updating or be aware of the existence of these components. On the other hand, if each component checks whether it should respond, all components would end up performing this check, which is inefficient.

This situation can be resolved by using a subclass of KoiProvider, the KoiTransmitter component, which allows creating a copy of the KoiProvider data or a custom data set based on the KoiProvider data.

In the example here, the KoiTransmitter and KoiConnectedFormFieldString components are connected to the KoiProvider and receive initial data values from it upon initialization. However, when the input field value changes, the controller modifies the data in the KoiTransmitter, not in the KoiProvider. As a result, the controller contains both the original data provided by KoiProvider and the modified data in KoiTransmitter.

Thus, the data flow is not cyclical, and the controller can use both data sets, including the resulting modified one, as required by the task.

In the case of the list described above, changing a single row in the list no longer triggers responses from all the rows in the list, because the result of the change is stored separately from the source of the original data that formed the list.