Creating a Custom Watchlist using AngularJS - ben-vargas/servicenow-wiki GitHub Wiki
This article explains how to create a custom watchlist on a ServiceNow form using AngularJS. This allows you to implement a user-friendly interface for adding and removing watchers, synchronized with a hidden field for server-side processing. This approach provides a more flexible and interactive alternative to the standard watchlist functionality.
The standard ServiceNow watchlist functionality can be somewhat limited in terms of user interface customization and direct manipulation. Creating a custom watchlist allows for more control over the presentation and user experience, potentially improving user efficiency.
This solution leverages AngularJS, a powerful JavaScript framework, to build an interactive watchlist component directly within a ServiceNow form. The watchlist displays added users, allows removal of watchers, and automatically synchronizes the watchlist state with a hidden ServiceNow form field.
This solution involves the following components:
- UI Macro: A UI Macro contains the HTML, CSS, and AngularJS code for the custom watchlist component.
- Client Script: A client script on the form handles the initial setup if necessary. (Usually not needed with this solution)
- Hidden Form Field: A hidden field on the form to store the watchlist data in a comma-separated list.
Here's the UI Macro code:
<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<script language="javascript" src="angular_min_js.jsdbx" />
<script>
var watchlist = angular.module('watchlist', []);
watchlist.controller('watchlistController', ['$scope', '$log',
function($scope, $log) {
$scope.watchlist = [];
// Add an entry to the watchlist array, used by the 'Add Watcher' button
$scope.addWatcher = function() {
var watchListSnField = g_form.getValue('watchlistUser');
if (!watchListSnField) {
return;
}
if ($scope.watchlist.indexOf(watchListSnField) == -1) {
$scope.watchlist.push(watchListSnField);
}
};
// Remove an entry in the array; used by "x" button click
$scope.removeWatcher = function(removeIdx) {
$scope.watchlist.splice(removeIdx, 1);
};
// Angular watch list to synchronize the watchlist var with SN watchlist_save variable/field
$scope.$watchCollection('watchlist', function(newValue, oldValue) {
$log.debug('Changed!');
$log.debug('Old:' + oldValue);
$log.debug('New:' + newValue);
g_form.setValue('watchlist_save', newValue.toString());
});
}
]);
</script>
<div ng-app="watchlist">
<div ng-controller="watchlistController">
<div>
<input type="hidden" class="form-control" ng-model="newWatchMember" />
<button ng-click="addWatcher()" type="button" class="btn btn-primary" style="margin-bottom:10px;">Add Watcher</button>
</div>
<ul class="list-group" ng-model="watchlist">
<li class="list-group-item" ng-repeat="watcher in watchlist" style="padding-left:6px;padding-bottom:2px;padding-top:2px;font-size:12px;padding-right:6px;">{{watcher}}<span ng-click="removeWatcher($index)" style="cursor:pointer;float:right;margin-right:16px;">x</span></li>
</ul>
</div>
</div>
</j:jelly>
Explanation:
-
Include AngularJS Library:
-
<script language="javascript" src="angular_min_js.jsdbx" />
includes the AngularJS library. Ensure that theangular_min_js.jsdbx
is uploaded as a db_image record into the instance.
-
-
AngularJS Module and Controller:
-
var watchlist = angular.module('watchlist', []);
creates a new AngularJS module named 'watchlist.' -
watchlist.controller('watchlistController', ['$scope', '$log', function($scope, $log) { ... }]);
defines a controller namedwatchlistController
.-
$scope
is used to access data and functions within the view. -
$log
is used for debugging messages.
-
-
-
$scope.watchlist
:- This array holds the current list of watchers.
-
$scope.addWatcher()
Function:- This function is called when the "Add Watcher" button is clicked.
-
var watchListSnField = g_form.getValue('watchlistUser');
gets the value from the variable name 'watchlistUser'. -
if (!watchListSnField) { return; }
Checks to ensure a user has been selected. -
if ($scope.watchlist.indexOf(watchListSnField) == -1) { $scope.watchlist.push(watchListSnField); }
adds the user to the array if it is not already present.
-
$scope.removeWatcher()
Function:- This function is called when the "x" button is clicked next to a user's name.
-
$scope.watchlist.splice(removeIdx, 1);
removes the watcher from the array using its index.
-
$scope.$watchCollection('watchlist', function(newValue, oldValue) { ... });
- This watches for any changes to the
watchlist
array. - It logs changes to the console for debugging.
-
g_form.setValue('watchlist_save', newValue.toString());
updates the hidden field named 'watchlist_save' with a comma-separated list of the current watchers.
- This watches for any changes to the
-
HTML Structure:
-
<div ng-app="watchlist">
specifies the AngularJS app. -
<div ng-controller="watchlistController">
links to the AngularJS controller. -
<input type="hidden" class="form-control" ng-model="newWatchMember"/>
creates a hidden form field for internal use by the angular controller. -
<button ng-click="addWatcher()" ...>Add Watcher</button>
creates the button that callsaddWatcher()
when clicked. -
<ul class="list-group" ng-model="watchlist">
creates an unordered list to display the watchers and to manage the watchlist values. -
<li class="list-group-item" ng-repeat="watcher in watchlist" ...>{{watcher}}<span ng-click="removeWatcher($index)" ...>x</span></li>
displays the list of watchers and the "x" button that callsremoveWatcher()
when clicked.
-
-
Create a UI Macro:
- Navigate to System UI > UI Macros.
- Create a new UI Macro with a name (e.g.,
custom_watchlist
). - Copy the code from the "Code Snippets" section above into the UI Macro's XML field.
-
Upload the Angular JS file:
- Download
angular.min.js
from an online source. - Navigate to System UI > Images
- Create a new image record named
angular_min_js.jsdbx
- Add the
angular_min.js
file as the image.
- Download
-
Create Hidden Field:
- Add a new hidden field (e.g.,
watchlist_save
) to your form where you will be using this UI Macro. This field is used for saving the state of the watch list.
- Add a new hidden field (e.g.,
-
Add UI Macro to the Form:
- Navigate to your form, and go to Form Layout.
- Drag and drop the UI Macro
custom_watchlist
to your form where you want the watchlist to appear. Ensure you are adding this as a new element, and not inside an existing field.
-
Add a User Reference Field:
- Add a reference field on the form where you want to select the user to add as a watcher and name this
watchlistUser
. - If you would like to display the watchlist on a different table, ensure this field is in that table.
- Add a reference field on the form where you want to select the user to add as a watcher and name this
- AngularJS Library: Ensure that the AngularJS library is loaded correctly in your ServiceNow instance.
- Hidden Field: The hidden field is used to persist watchlist data when the form is saved.
- Form Placement: Add UI Macros as new form elements and not inside existing fields to avoid layout issues.
-
Error Handling: Consider adding error handling, especially in the
$scope.addWatcher()
function. - Performance: Keep the number of watchers in a reasonable amount to avoid performance issues.
- UI Styling: Customize the CSS to match your branding.
- Security: Ensure users have access to the tables that are being manipulated.
- Be aware of any security issues when passing user ids on the client side, make sure to validate this on the server side to ensure no unauthorized user access.
- Ensure the hidden field is not accessible to users who should not have access to the data.
This custom watchlist provides a more user-friendly and interactive experience compared to the standard watchlist field. By using AngularJS, you can easily build a more dynamic and customizable UI component on a ServiceNow form. Remember to test your UI Macro thoroughly in a non-production environment before deploying to production. This solution promotes flexibility and provides a basis for implementing more complex interactive elements on your ServiceNow forms.