Skip to content

Commit a110d1b

Browse files
authored
MRVS observer (ServiceNowDevProgram#870)
* Create MRVSUtils.js UI Script holding observer class * Create readme.md * Update readme.md * Update MRVSUtils.js
1 parent 28110d1 commit a110d1b

File tree

2 files changed

+184
-0
lines changed

2 files changed

+184
-0
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
var MRVSUtils = Class.create();
2+
3+
// Default configuration for the MutationObserver
4+
MRVSUtils.OBSERVER_CONFIG = {
5+
subtree: true,
6+
childList: true
7+
};
8+
9+
MRVSUtils.prototype = {
10+
/*
11+
* initialise the MRVSUtils instance
12+
* @param {String} Name of multi-row variable set to watch
13+
*/
14+
initialize: function (mrvsName) {
15+
this.mrvsID = g_form.getControl(mrvsName).id;
16+
this.mrvsID = this.mrvsID.replace(/(.*:)/, '');
17+
this.table = document.getElementById(this.mrvsID + "_table");
18+
this.columnNames = this._getColumnNames();
19+
this.rowIds_Old = [];
20+
this.rowIds = [];
21+
this.isUpdate = false;
22+
},
23+
24+
/*
25+
* Process all mutations from the observer
26+
* @param {MutationList} Array of all mutations observed
27+
* @returns {JSON} Array of updates
28+
*/
29+
processMutations: function (mutations) {
30+
this._updateRowIds();
31+
var mutationList = this._filterEvents(mutations);
32+
return this._processMutation(mutationList[0]);
33+
},
34+
35+
/*
36+
* Get the table ID for the multi-row variable set
37+
* @return {string} (sys_id)_table
38+
*/
39+
getTableID: function () {
40+
return [this.mrvsID, "table"].join('_');
41+
},
42+
43+
/*
44+
* get list of column names
45+
* used when building the modified data JSON
46+
*/
47+
_getColumnNames: function () {
48+
var columnNames = [];
49+
var headers = this.table.getElementsByTagName('th');
50+
for (var _col = 0; _col < headers.length; _col++) {
51+
columnNames.push(headers[_col].innerText);
52+
}
53+
54+
return columnNames;
55+
},
56+
57+
/*
58+
* re-scan the MRVS table to get an up to date list of rows
59+
*/
60+
_updateRowIds: function () {
61+
this.rowIds = [];
62+
var body = this.table.getElementsByTagName('tbody');
63+
var rows = body[0].getElementsByTagName('tr');
64+
for (var _row = 0; _row < rows.length; _row++) {
65+
this.rowIds.push(rows[_row].id);
66+
}
67+
},
68+
69+
/*
70+
* check if this is an mutation we care about
71+
* basically, if it's a update to TBODY and has either added or removed nodes,
72+
* or it's a updated row which should give us both added and removed for a single TD (however this is
73+
* not guaranteed if the original cell value was empty)
74+
*/
75+
_isUpdateEvent: function (mutation) {
76+
var isAddDelete = ((mutation.target.nodeName == 'TBODY' && mutation.target.id == 'empty_table_row') &&
77+
((mutation.removedNodes.length > 0 && mutation.removedNodes[0].className != 'list2_no_records') ||
78+
(mutation.addedNodes.length > 0 && mutation.addedNodes[0].className != 'list2_no_records')));
79+
var isUpdate = (mutation.target.nodeName == 'TD' && (mutation.removedNodes.length > 0 || mutation.addedNodes.length > 0));
80+
return isAddDelete || isUpdate;
81+
},
82+
83+
/*
84+
* for the given node, get the cells contents
85+
*/
86+
_getCellData: function (node, rowIds) {
87+
var cellData = {};
88+
var cells = node.cells;
89+
for (var i = 0; i < cells.length; i++) {
90+
if (cells[i].className == 'vt') cellData[this.columnNames[i]] = cells[i].innerText;
91+
}
92+
93+
cellData.row_number = rowIds.indexOf(node.id) > -1 ? rowIds.indexOf(node.id) + 1 : undefined;
94+
cellData.row_id = node.id;
95+
return cellData;
96+
},
97+
98+
/*
99+
* process the given nodes (either added or deleted) and build the response using the cell data
100+
*/
101+
_getNodeData: function (nodes, rowIds) {
102+
var modifiedRows = [];
103+
nodes.forEach(function (_node, _index, _list) {
104+
modifiedRows.push(this._getCellData(_node, rowIds));
105+
}, this);
106+
return modifiedRows;
107+
},
108+
109+
/*
110+
* given a mutation, build the JSON response depending on the mutation type
111+
*/
112+
_processMutation: function (mutation) {
113+
var modifiedData = {};
114+
if (this.isUpdate) { // special handling as some updates only have a single event
115+
// this is a row update
116+
modifiedData.updated = this._getCellData(mutation.target.parentElement, this.rowIds);
117+
} else if (mutation.addedNodes.length && mutation.removedNodes.length == 0) {
118+
modifiedData.added = this._getNodeData(mutation.addedNodes, this.rowIds);
119+
} else {
120+
// pass in the old row_id list otherwise we won't know what row was release.
121+
modifiedData.removed = this._getNodeData(mutation.removedNodes, this.rowIds_Old);
122+
}
123+
124+
this.rowIds_Old = this.rowIds;
125+
return modifiedData;
126+
},
127+
128+
/*
129+
* filter the mutation list for mutations we care about
130+
* If only one mutation is provided then it's assumed it's a row update (based on extensive testing!!)
131+
*/
132+
_filterEvents: function (mutationList) {
133+
this.isUpdate = false;
134+
if (mutationList.length > 1) {
135+
mutationList = mutationList.filter(function (_mutation) {
136+
return this._isUpdateEvent(_mutation);
137+
}, this);
138+
} else {
139+
this.isUpdate = (mutationList[0].addedNodes.length > 0 && mutationList[0].removedNodes.length > 0);
140+
}
141+
return mutationList;
142+
}
143+
};
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Observe Multi-row variable set events
2+
3+
Using the MutationObserver API we can monitor changes to a multi-row variable set (i.e., new rows, deleted rows and updated rows).
4+
This currently only works in the platform, not Workspace or Service Portal.
5+
6+
Use in a onLoad client script (Isolate script = false). Sets up an observer on the named variable set and any changes are returned in the mutationList object.
7+
Return value will list changes to the variable set. For example:
8+
9+
```json
10+
{
11+
"removed": [
12+
{
13+
"VM #": "2",
14+
"Name": "2",
15+
"row_number": 1,
16+
"row_id": "row_9652c56347f5311001612c44846d433f"
17+
}
18+
]
19+
}
20+
```
21+
22+
23+
```javascript
24+
25+
function onLoad() {
26+
27+
setTimeout(function() {
28+
var mrvs = new MRVSUtils('name of multi-row-variable-set');
29+
var observer = new MutationObserver(function(mutationList, observer) {
30+
var modifiedData = mrvs.processMutations(mutationList);
31+
console.log(JSON.stringify(modifiedData, '', 3));
32+
});
33+
34+
// create the observer looking for changes to the contents of the MRVS
35+
observer.observe($(mrvs.getTableID()), MRVSUtils.OBSERVER_CONFIG);
36+
37+
}, 1000);
38+
39+
}
40+
```
41+

0 commit comments

Comments
 (0)