Precursor
Record watchers have been around for quite some time to provide real-time data on back-end forms in ServiceNow as well as in the Service Portal. Changes in instance data trigger an event on the front end, upon receiving the event you can then perform logic based on the change to instance data.
Similarly to making REST API calls there is a predefined npm package you can install to work just like @servicenow/ui-effect-http
. This package is named @servicenow/ui-effect-amb
and you will have to install it to utilize record watching functionality.
Record watchers can not truly be tested until the components are deployed to the instance directly. This is because there is no way to use the proxy from now-cli.json in websockets which is the paradigm that record watchers use.
This can make debugging quite difficult, so I apologize for the inconvenience.
Preparation
First things first you need to install the package.
npm install @servicenow/ui-effect-amb
This package needs to be imported to create an actionHandler
similar to when you import @servicenow/ui-effect-http
.
import { createAmbSubscriptionEffect } from "@servicenow/ui-effect-amb";
The function createAmbSubscriptionEffect
takes two main parameters, a channelid
string and an options
object. Below is the definition of the effect and explains which parameters it expects.
createAmbSubscriptionEffect
/*
* @param {String} channelId - AMB Channel ID
* @param {Object} options
* @param {String} options.subscribeStartedActionType - Action dispatched when an amb subscription is started
* @param {String} options.subscribeSucceededActionType - Action dispatched if an amb subscription succeeds
* @param {String} options.subscribeFailedActionType - Action dispatched if an amb subscription fails
* @param {String} options.unsubscribeSucceededActionType - Action dispatched when an amb unsubscribe is successful
* @param {String} options.messageReceivedActionType - Action dispatched when a message is received on a channel
* @param {Boolean} options.encodeURIComponentForChannelId - Encode channel id URL (default: true)
*
* @return {Object} AMB Subscription Effect Descriptor
*/
Channel ID
The channel ID is almost always going to be the same string because it essentially serves as the endpoint to kick off the record watcher.
//! This string will almost always be:
const channelID = "/rw/default/:table/:filter";
Usage
This function will return an object that can be attached directly to an actionHandler
. The action handler that it is attached to will serve as the main way to start and stop a record watcher.
To make life easier I created a helper function which generates an ambSubscriptionEffect
and fills in some default settings. Generally you will only need to really use the subscribeStartedActionType
and messageReceivedActionType
properties in the options
object.
ambEffectHelper.js
import { createAmbSubscriptionEffect } from "@servicenow/ui-effect-amb";
const channelID = "/rw/default/:table/:filter";
export const createComponentWatcher = (prefix = "DEFAULT_PREFIX", options) => {
return createAmbSubscriptionEffect(channelID, {
subscribeStartedActionType: `${prefix}_WATCHER_DEFAULT_STARTED`,
subscribeSucceededActionType: `${prefix}_WATCHER_DEFAULT_SUBSCRIBED`,
subscribeFailedActionType: `${prefix}_WATCHER_DEFAULT_FAILED`,
unsubscribeSucceededActionType: `${prefix}_WATCHER_DEFAULT_UNSUBSCRIBED`,
messageReceivedActionType: `${prefix}_WATCHER_DEFAULT_CHANGED`,
...options,
});
};
Now that we have our helper function set up we can quickly build actionHandlers
to start or stop a record watcher subscription.
actionHandlers.js
import { createComponentWatcher } from "../ambEffectHelper";
export default {
actionHandlers: {
//┌─────────────────────────────────────────────────────────────
//! This will beging the record watcher subscription
//! an important detail to note is the subscribe: true
//! in the dispatch call.
//└─────────────────────────────────────────────────────────────
START_RECORD_WATCHER: ({ state, dispatch }) => {
const recordTable = "incident";
const recordID = "7f96bd071b32c010f89b99bc1d4bcbf9";
const originalFilter = `state=1^sys_id=${recordID}`;
const filter = btoa(originalFilter).replace(/=/g, '-');
dispatch(`SETUP_RECORD_WATCHER`, {
table: recordTable,
filter: filter,
subscribe: true,
});
},
//┌─────────────────────────────────────────────────────────────
//! This actionHandler is nearly the same, but stops the
//! record watcher instead of starting it.
//└─────────────────────────────────────────────────────────────
STOP_RECORD_WATCHER: ({ state, dispatch }) => {
const recordTable = "incident";
const recordID = "7f96bd071b32c010f89b99bc1d4bcbf9";
const originalFilter = `state=1^sys_id=${recordID}`;
const filter = btoa(originalFilter).replace(/=/g, '-');
dispatch(`SETUP_RECORD_WATCHER`, {
table: recordTable,
filter: filter,
subscribe: false,
});
},
//┌─────────────────────────────────────────────────────────────
//! This actionHandler allows you to start and stop
//! a record watcher using our helper function.
//└─────────────────────────────────────────────────────────────
SETUP_RECORD_WATCHER: createComponentWatcher("RECORD", {
subscribeStartedActionType: `RECORD_WATCHER_STARTED`,
messageReceivedActionType: `RECORD_WATCHER_ITEM_CHANGED`,
}),
//┌─────────────────────────────────────────────────────────────
//! This is really just to let you know a subscription
//! to a record watcher has been started. Serves no other
//! purpose.
//└─────────────────────────────────────────────────────────────
RECORD_WATCHER_STARTED: (coeffects) => {
console.log(`RECORD_WATCHER_STARTED`, coeffects);
},
//┌─────────────────────────────────────────────────────────────
//! This is the most important actionHandler, it will be
//! triggered any time a record that fits the query is
//! inserted/updated/deleted.
//!
//! From here you can perform any logic you want.
//└─────────────────────────────────────────────────────────────
RECORD_WATCHER_ITEM_CHANGED: ({ action, dispatch }) => {
const { payload } = action;
console.log(`RECORD_WATCHER_ITEM_CHANGED`, payload);
const { data: { operation } } = payload;
//! Do something based on the operation
//! insert | update | delete
dispatch(`RECORD_WATCHER_${operation}`, payload);
},
}
}
Conclusion
With this basic setup you can now perform any logic you want when a record is inserted, updated or deleted within the encodedQuery
you define for your record watcher.
Since components can't always be directly attached to each other, this is a nice way to form a basic type of communication between different components.
Disclaimer
Again, the only way to truly test to see if your record watcher is working is to deploy your component to your instance, this a major detractor in the setup process, but with some effort you too can begin record watching!
Top comments (0)