Components can be divided into three main types:
- Components that manage data. These components are responsible for retrieving, storing, and saving data. Since these components contain objects that describe the data of a particular domain, they are also responsible for domain-specific operations.
- Components that display data. These components focus on user interaction. They present the data in a user-friendly format and respond to user actions.
- Universal components. These components manage data and interact with the user using that data.
To achieve efficiency and flexibility, each of these components should be broken down into behaviors, each responsible for a specific task. This allows for easy combination of behaviors when creating a new component, resulting in a component of the desired type.
When behaviors are divided into categories like data management, data display, and workflow coordination, this division aligns with the internal MVC component pattern.
Object of Own Data
As discussed in the Composition section, to implement behavior of the "has-a" type, a class attribute should be created, and during initialization, this attribute is assigned an object that will implement the behavior.
In the case of behavior responsible for data management, it is necessary to determine whether the component will manage data belonging to itself or to another component. This introduces the concept of an "object of own data."
The object of own data is responsible for storing the component's current state. Own data can include displayed values, current input field values, the state of toggles, a list of selected items, etc. For example, the object of own data in the KoiLabel component stores the currently displayed data.
The data object must ensure proper data handling, including validation and formatting. For instance, after receiving data from the user and before passing it to higher levels of the application, the data should be validated, and the validation flag should be updated.
The data object can also perform domain-specific operations. For example, a multiplier object not only stores two factors but also provides a method that returns their product. An object managing a shopping list might provide a method that returns the total expenses.
In the KoiCom library, objects of own data are subclasses of the KoiData object, which defines the data structure, performs automatic data validation, and provides a set of additional methods for data management.
Data Display Object
The data display object provides the component’s template and methods to populate it. Typically, these are two types of methods: methods that ensure the initial population of the template before it is shown to the user, and methods that update the template’s content based on changes in data or other events.
Additionally, the data display object must provide methods for displaying non-standard situations to the user, allowing the component to demonstrate the data loading process, show error notifications, and so on.
Since a component can include other components, the data display object should have methods to manage these components. For example, if an error notification is displayed within the component in a container, the data display object should have a reference to the container and issue a command to display the notification. In this case, the data display object should encapsulate the reference to the container and its methods, exposing only its own method for error notification.
In the KoiCom library, the following analogy is used: the main component is represented as a motherboard, to which a display is connected via a socket. From the motherboard’s perspective, one display can be swapped for another, and communication occurs not with the display, but with the socket. Therefore, the data display object in KoiCom is called Socket.
It’s important to note that the data may not necessarily be displayed within the component itself. External components can be connected to the socket. When external components are connected to the component, the component does not require a template. As a result, the behavior providing the component with the template object is separated and named KoiSocketTemplateCapable.
Controller
The component itself serves as the controller, representing a composition of the behaviors described above. The specific behaviors implemented by the component depend on the task.
If the component is a universal composition, it facilitates the redirection of data from one object to another. For example, KoiLabel takes data from an instance of KoiStringData and directs it to KoiLabelSocket.
Since any object can be replaced with another object of the same type depending on the task, the methods responsible for the component’s interaction with its objects also change. Therefore, it is convenient to move these methods into behaviors using mixins.
Thus, the DocsMultiplyLabel component looks like this:
In this way, each component implements the MVC pattern. The component itself acts as the controller and can include both the data object and the data display object, interacting with each through an interface rather than directly. These interfaces are implemented as mixins.
Components without Display Objects
A component can not only lack a template but also lack a socket (display object). Such a component works solely with data and is referred to as a data provider in the KoiCom library.
In fact, many frameworks and libraries operate without providers, focusing only on the data objects themselves. However, there is a reason why providers are necessary.
Data management tasks include not only data storage and domain-specific tasks but also tasks such as data retrieval and validation. A data object does not handle all aspects of data management. It does not interact with external systems to fetch or send data, nor does it trigger events to notify system components about data changes. Additionally, it does not change its state based on the validity or completeness of the data.
The component responsible for interacting with the external environment and other components should be the one that contains the data object. This component acts as a controller, connecting the data object to the external environment and ensuring their interaction. Such a component in the KoiCom library is called a provider.
Components without Own Data
Components do not necessarily have their own data. Examples of such components include panels that contain other components, modal windows, certain buttons, and, of course, components that display external data.
While panels and modal windows are simple, components that display external data deserve additional consideration.
To facilitate communication between components, an object called a connector is used in the KoiCom library. Each connector is linked to a specific behavior (interface). For example, the KoiSingleConnector connector is associated with the behavior KoiSingleConnectorInteractable.
A connector can be connected to any component that has its own data. Its role is to retrieve the data from the component it is connected to.
The connection is established at the time the connector object is created. The connector object is instantiated within the component's constructor.
Here, the component that uses the connector calls the _onConstructed method in its own constructor. In this method, it retrieves the provider's identifier from the tag's attributes and passes it to the connector's constructor. After that, the component can use the class attribute this._connector to fetch data from the provider.
Connectors come in two types: those that allow connection to a single provider and those that allow simultaneous connections to multiple providers.
Summary
To summarize, in the KoiCom library, the MVC pattern is used to separate the behaviors of components. Each component can be constructed as a composition of these behaviors. There are two main types of behaviors: those for handling data and those for displaying data. In the KoiCom library, these behaviors are named *DataCapable and *SocketConnectable. In a sense, behaviors act as interfaces for data objects and display objects. A component that handles only data is called a data provider. A component without its own data can use data from other components, and for this, it requires a connector object.