Kogito Tooling Examples —How to create a custom View
This is part of a series of blog posts, and this one covers “How to create a custom View”.
You can navigate through the series by clicking on the following topics:
- How to create a custom Editor in a React application;
- How to create a Chrome Extension for a custom Editor;
- How to create a VS Code Extension for a custom Editor;
- How to create a custom View;
- How to create a VS Code Extension for a custom View;
- How to create a more complex custom View;
- How to integrate a custom Editor, an existing Editors, and custom Views on a Web App;
In this section, we’re going to cover how to create a custom View. If you have questions about what is a View (read more about it here), but to summarize it, a View is an Envelope. Or, to be more specific, an iframe element with your code inside and a strongly-typed communication interface with the Channel.
Like our previous sections, we’re going to utilize some React concepts to create and utilize Views (especially React hooks), so in case you’re not familiar with it, it’s suggested that you read the React documentation.
Starting
A custom View has three essential “submodules”, which are separated in directories:
- api: the API definition, which is used to communicate with the Channel.
- embedded: the Embedded Envelope component implementation, which is used by the Channel. (this is specific to React)
- envelope: this custom View’s Envelope implementation.
In this first example, we’ll create a ‘To-do’ List View, which will enable the Channel to use this View at any place. This View will feature adding, removing and marking items as complete.
API
The ‘To-do’ List View api
submodule has the following files:
TodoListViewApi.ts
This is the API of your View as a component. It exposes methods that allow you to control your View component.
TodoListViewChannelApi.ts
This is the API of a Channel where this View’s Envelope can run on, and it’s consumed by the Envelope.
TodoListViewEnvelopeApi.ts
And at least, this is the API of your component’s Envelope, which is consumed by the Channel.
Embedded
This is a convenience submodule specific for React. Here is the implementation of the EmbeddedTodoList
. It’s a component used by the Channel, which makes the communication between the Channel and the Envelope. Hence, it connects the interfaces from the api
directory. To create this component, we’ll utilize the EmbeddedEnvelope
component provided by Kogito Tooling.
RefForwardingComponent
To start, we’ll use a RefForwardingComponent
which provides a way to access the component imperatively and accessing its exposed methods. Also, it enables the usage of React hooks.
Here the forwardedRef
implements the TodoListApi
. The props implement the TodoListChannelApi
with two new attributes, which are used to tell the EmbeddedEnvelope
component where the Envelope is located.
PoolInit
To start the communication between Channel and Envelope, we need to create a method used on the initialization. This method is called poollInit
, and it receives an EnvelopeServer
instance typed with the Channel and Envelope API’s. The EnvelopeServer
is responsible for sending messages from the Channel to the Envelope (strictly in this direction), and receiving messages from the Envelope too. The pollInit
method tells the EnvelopeServer
what to do to “connect” with the Envelope. It can contain any parameters and this method can be called many times, since the Envelope can take a while to load. Beware of side-effects!
refDelegate
The methods implemented on the TodoListApi
interface are going to be exposed through this component reference. In this example, the EnvelopeServer
has the implementation of the methods. To abstract the EnvelopeServer
instantiation, we delegate the exposure of the component reference to the EmbeddedEnvelopeFactory
, which already has an EnvelopeServer
instance, making the EmbeddedTodoList
a lot simpler.
EmbeddedEnvelope
To create an EmbeddedEnvelope
component, we utilize an available factory from Kogito Tooling, which will create the component, initializing the EnvelopeServer
with the pollInit
and then exposing the methods on the TodoListApi
interface through the refDelegate
method.
Envelope
All the code that runs inside the Envelope is here. The ‘To-do’ List Envelope is separated in:
- TodoListEnvelope.tsx
- TodoListEnvelopeApiImpl.tsx
- TodoListEnvelopeContext.tsx
- TodoListEnvelopeView.tsx
TodoListEnvelope
This file has the function responsible for initializing the Envelope. To start, it will create an Envelope with the ‘To-do’ List types.
Now, we create a method that knows how to render a TodoListEnvelopeView
and returns a function with its API. In this example, we’re using a React View, but it’s possible to use any other framework here or just plain Javascript, giving flexibility to your implementation.
To finalize, we start the Envelope passing the View, the context, and a factory, which instantiates an Envelope API. In this example, our factory is not generic, and can only instantiate the same Envelope Api, but further, in another example, we’re going to see a more advanced factory implementation. A context is an empty object because of this example’s simplicity, but you can check it out an implementation on the Kogito Tooling for a more advanced implementation.
TodoListEnvelopeApiImpl
This file has the implementation of the Envelope API, and it receives an EnvelopeApiFactoryArgs
. It has access to the View API, the EnvelopeContext
, and the EnvelopeBusController
. The EnvelopeBusController
is the class responsible for the communication from the Envelope to the Channel (strictly this flow of messages).
The Envelope API implementation relies on the View API except for the todoList__init
method. It will associate the actual Envelope with the Channel and set the user on our View (TodoListEnvelopeView
).
TodoListEnvelopeView
This file has what is going to be rendered on the View. Also, it has the View API, which is consumed by the TodoListEnvelopeApiImpl
file.
Like our other components in this post, we will be using the React RefForwardingComponent
, and now we typed it with the TodoListEnvelopeViewApi
. Additionally, we created an user
state to have a more friendly interface, and an items state, which has the list of all items on our ‘To-do’ List.
Utilizing the userImperativeHandle
we expose the TodoListEnvelopeViewApi
methods.
To manipulate our ‘To-do’ List View, we created some useful callbacks with the userCallback
hook.
removeItem
will create a copy of theitems
array, so it doesn’t modify the current state’s value. Using the copied array, it’ll search for the specified item, and then remove it by setting a state with the updated array (before the removal).updateItemCompletedStatus
will also create a copy of the currentitems
array. It will search for the specified item and then update the state with the updated array.allCompleted
is a memoized value updated every time theitems
state is changed. It checks if all items have thecompleted
property equals to true.
To finalize our View, the only missing part is what is going to be rendered. We’re not going to cover the specifics of this implementation because it involves personal choices. This is the look of the final implementation.
Wrapping up
Now you know how to create your own custom View. In the next section, we’ll see how to Embed it on a VS Code Extension.
Thanks for reading!