We have looked at the various visual elements available in the UIKit module. Now we will learn how to create them in our apps and handle user interactions through examples. Interactions can take different forms. They can be invoked with slash commands, buttons, or automated at regular intervals.
Interaction flow
When a user interacts with any UI components, it triggers other interactions. Based on the UI component being used, the app receives an interaction of the specified type, and the app gets the context of what the interaction was, what data was filled, the inputs provided by the user, and so on.
A typical interaction flow looks something like this:
A user takes an action that invokes an associated trigger.
Your app processes the action.
Then, your app provides the associated response.
This cycle can go on till the final result is achieved.
Types of interactions
You can create interactions according to your needs. Some available options are:
Slash commands that users can enter to access the app’s features.
Event interfaces and handlers that can be used to execute an action based on a user or system action.
Displaying UI blocks based on a user or system action.
Adding app action buttons that can be used to access app features.
Creating interactive UI, such as forms that receive and process user input.
Typically, you will use multiple elements and interactions in an app to produce the desired results.
The following information provides details about handling interactive UI in your apps.
Interaction payload
Before an interaction can happen, the app must define the UI. You do this by defining the app's UI building blocks. Interaction parameters are added while defining the blocks and block elements, such as action ID and block ID. Refer to the Interaction parameter details section in the Building Blocks document for details.
Handling UIKit interactions
The IUIKitInteractionHandler interface must be implemented by the main App class to handle interactions. When a user interacts with the UI, Apps-Engine calls one of four handler methods and sends the interaction payload. You choose which to implement by including them in your class. All four implement the IUIKitInteractionHandler interface. The methods are as follows:
executeBlockActionHandler: Method called when a block action is invoked. It is called whenever a user interacts with an interactive element (button, select, overflow menu, date picker). This is the most frequently used handler.executeActionButtonHandler: Method called when an action button is clicked.executeViewSubmitHandler: Method called when a modal is submitted. This is called when the user clicks the primary submit button of a modal or contextual bar. This is where you read form field values, validate them, and act on the data.executeViewClosedHandler: Method called when a modal is closed. It is called when the user dismisses a modal or contextual bar without submitting, by pressing the cancel button, pressing Escape, or clicking outside the UI surface. Common uses include logging analytics events, cleaning up persisted draft state, or displaying a "you may have unsaved changes" warning via a follow-up message.
For further details on the methods, refer to the Rocket.Chat Apps TypeScript Definition.
For example, the following code snippet shows a sample structure of the main App class:
import { App } from '@rocket.chat/apps-engine/definition/App';
import {
IUIKitInteractionHandler,
UIKitBlockInteractionContext,
UIKitViewSubmitInteractionContext,
UIKitViewCloseInteractionContext,
IUIKitResponse
} from '@rocket.chat/apps-engine/definition/uikit'; // Importing the necessary interfaces
// Add any other required interfaces
export class MyApp extends App implements IUIKitInteractionHandler { // Implement the IUIKitInteractionHandler interface
constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) {
super(info, logger, accessors);
}
public async executeBlockActionHandler(
context: UIKitBlockInteractionContext,
read: IRead,
http: IHttp,
persistence: IPersistence,
modify: IModify
): Promise<IUIKitResponse> {
// ... function definition goes here
return context.getInteractionResponder().successResponse();
}
public async executeActionButtonHandler(
context: UIKitActionButtonInteractionContext,
_read: IRead,
_http: IHttp,
_persistence: IPersistence,
modify: IModify,
): Promise<IUIKitResponse> {
// ... function definition goes here
return context.getInteractionResponder().successResponse();
}
public async executeViewSubmitHandler(
context: UIKitViewSubmitInteractionContext,
read: IRead,
http: IHttp,
persistence: IPersistence,
modify: IModify
): Promise<IUIKitResponse> {
return context.getInteractionResponder().successResponse();
}
public async executeViewClosedHandler(
context: UIKitViewCloseInteractionContext,
read: IRead,
http: IHttp,
persistence: IPersistence,
modify: IModify
): Promise<IUIKitResponse> {
return context.getInteractionResponder().successResponse();
}
}As shown in the code sample, each method receives five standard parameters, described as follows:
Parameter | Interface | Description |
|---|---|---|
| The interface depends on the method used.
| This accessor includes all data about the interaction. |
|
| This accessor has read-only access to rooms, users, messages, settings, and persistence. |
|
| This accessor is used to make outbound HTTP requests (REST APIs, webhooks). |
|
| This accessor is used for the app's persistence storage. |
|
| This accessor is used to make modifications with the app, such as sending messages and opening another UI block. |
Interaction responses
Each handler method must return IUIKitResponse. Use context.getInteractionResponder() to create the appropriate response. The response determines what happens to the UI after the interaction. The following options are available for returning responses:
successResponse(): This closes the current interaction. No visible changes are made to the UI beyond any message updates that you may have defined. This is the most common return value. The following code snippet shows the format of the response that you need to include:
return context.getInteractionResponder().successResponse();viewErrorResponse(): You can use this in cases when the user enters incorrect input or leaves a mandatory field blank. This keeps the UI surface open and highlights the specific input fields with error messages. The user sees an error message displayed below the input field without the modal closing, based on what you have added in your code. The following code snippet shows an example:
return context.getInteractionResponder().viewErrorResponse({
viewId: data.view.id,
errors: {
name: 'Name is required.',
category: 'Please select a category.',
// here, the key is the actionId of the input field to highlight
// value is the the error message to display
},
});Each handler invocation is stateless. You must explicitly persist any data you want available across interactions. Refer to the App Data Persistence guide to learn how to save data between sessions.
Explore the examples in the sub-section and learn how to create visual elements and handle user interactions.