Custom Entity Collect Extension in Oracle Policy Automation

In this second part of the series, we continue to investigate building a Custom Entity Collect Extension in Oracle Policy Automation, and complete the basic example of a working collector. The code is once again focused on pointing out big ideas, traps, suggestions and the details are left to you, dear readers. Everything you see here is free and you can use it anywhere but it is for educational and entertainment purposes only. The PDF version of the code is available in the OPA Hub Shop for free.

Building the User Interface

The next big chunk of code is all about continuing the work begun previously, specifically building the User Interface by adding input boxes and other User Interface elements to the page. We switch according to data type, and as before we add a number of valuable extra HTML attributes to our elements.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//GET KEY INFORMATION
									var theinstancename = myrecords[i][j].instance.toString();
									var theentityname = myrecords[i][j].entity.toString();
									var theattributename = myrecords[i][j]._source.config.attributeId;
 
									var myname = myrecords[i][j].id.toString();
 
									attrInput.setAttribute('id', myId);
									attrInput.setAttribute('data-entityname', theentityname);
									attrInput.setAttribute('data-instancename', theinstancename);
									attrInput.setAttribute('data-attributename', theattributename);
									attrInput.setAttribute('data-rownumber', j.toString());
									attrInput.setAttribute('name', myname);
 
									// CONVERT ATTRIBUTES OBJECT TO ARRAY
									var myAttributeObject = myrecords[i][j]._source.screen.clientState[theentityname];
									myAttributeObject = myAttributeObject[theinstancename];
									var myAttributeArray = $.map(myAttributeObject, function (value, index) {
											return [value];
										});
 
									//DISPLAY THE CORRECT VALUE IN THE INPUT BOX
 
									switch (myattributedatatype) {
									case "boolean":
										var response;
										switch (myAttributeArray[j].toString()) {
 
										case "true":
											response = true;
											break;
										default:
											response = false;
										}
										attrInput.checked = response;
										rowdiv.appendChild(attrLabel);
										$(attrLabel).after(attrInput);
 
										break;
 
									case "number":
										attrInput.setAttribute('value', Number(myAttributeArray[j]));
										rowdiv.appendChild(attrLabel);
										$(attrLabel).after(attrInput);
										break;
 
									case "text":
										attrInput.setAttribute('value', myAttributeArray[j].toString());
										rowdiv.appendChild(attrLabel);
										$(attrLabel).after(attrInput);
										break;
									case "date":
										if ($(attrInput).attr("data-datepicker") === 'true') {
											$(attrInput).datepicker();
											$(attrInput).datepicker("option", "dateFormat", "yy-mm-dd");
											$(attrInput).val( myAttributeArray[j]);
											rowdiv.appendChild(attrLabel);
											$(attrLabel).after(attrInput);
 
 
 
										}
										break;
 
									}

Whoa, easy there! So what is all of that about? The different kinds of data (Boolean, text, numbers, dates) need to be handled slightly differently, to display correctly.

Error Display

What happens if the Interview cannot continue (note that this example Entity Collect only handles errors on navigate, the default for Oracle Policy Automation Interviews) because of an issue? The user needs to know what they have done wrong, so they can correct it. Thankfully the errors are available to us (if they exist).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//HANDLE DATA ENTRY ERRORS AND ERROR DISPLAY
var myerrorobject = myrecords[i][j]._source.screen.errors[theentityname]
if (myerrorobject) {
var myerrorinstance = myerrorobject[theinstancename];
if (myerrorinstance) {
var myerrorattribute = myerrorinstance[theattributename]
if (myerrorattribute) {
var errorrow = document.createElement("div");
errorrow.id = "rowcontainer" + i.toString();
errorrow.setAttribute('data-entityname', theentityname);
errorrow.setAttribute('data-instancename', theinstancename);
errorrow.setAttribute('data-attributename', theattributename);
$("#errordiv").append(errorrow);
 
$(errorrow).append("Attribute : " + myerrorattribute.attributeId.toString() + " - " + myerrorattribute.message);
$("input[data-entityname='" + theentityname + "'][data-instancename='" + theinstancename + "'][data-attributename='" + theattributename + "']").css("border", "3px solid red");
}
}
}

In this section we build a small DIV for the errors, and add any errors that exist as rows within it. At the same time, the relevant attribute is highlighted with a red border.

Updating the User Interface

When the user makes changes, we need to handle them and ensure they are passed on. The last part of the code for today concentrates on handling each of the data types, and making sure the data is correctly passed to Oracle Policy Automation. This is quite tricky, and can necessitate quite a bit of review of the Investigate JSON to try and understand what it does not like, as the error in the Browser will most likely be a generic 500 error.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
var inputtype = $(attrInput).attr("type");
var datepicker = $(attrInput).attr("data-datepicker")
 
switch (inputtype) {
case "checkbox":
$(attrInput).change(function () {
interview.setInputValue(event.target.attributes["data-attributename"].value, this.checked, event.target.attributes["data-entityname"].value, event.target.attributes["data-instancename"].value);
 
interview.saveData();
event.stopImmediatePropagation();
 
});
break;
case "input":
 
if (datepicker === 'true') {
$(attrInput).change(function () {
var mydate = $(this).datepicker('getDate');
mydate = new Date(mydate);
var mydateinstance = $(this).attr("data-instancename");
var mydateattribute = $(this).attr("data-attributename");
var mydateentity = $(this).attr("data-entityname");
var myisodate = mydate.toString();
function formatDate(mydate) {
var d = new Date(mydate),
month = '' + (d.getMonth() + 1),
day = '' + d.getDate(),
year = d.getFullYear();
 
if (month.length < 2)
month = '0' + month;
if (day.length < 2)
day = '0' + day;
 
return [year, month, day].join('-');
}
interview.setInputValue(mydateattribute, formatDate(mydate), mydateentity, mydateinstance);
 
interview.saveData();
event.stopImmediatePropagation();
 
});
 
} else {
$(attrInput).change(function () {
interview.setInputValue(event.target.attributes["data-attributename"].value, event.target.value.toString(), event.target.attributes["data-entityname"].value, event.target.attributes["data-instancename"].value);
 
interview.saveData();
event.stopImmediatePropagation();
 
});
 
}
 
break;
case "number":
$(attrInput).change(function () {
interview.setInputValue(event.target.attributes["data-attributename"].value, Number(event.target.value), event.target.attributes["data-entityname"].value, event.target.attributes["data-instancename"].value);
 
interview.saveData();
event.stopImmediatePropagation();
 
});
break;
}
 
}

And so this pretty much leaves us with a row builder, and an error builder. But the final part of the code is very important, specifically the Delete Button to delete the row you don’t want any more.

Delete Rows

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//ADD A DELETE ROW BUTTON  AFTER EACH ROW
								var deletebutton = document.createElement("button");
								deletebutton.setAttribute("type", "button");
								var deletebuttontext = document.createTextNode(control.getRemoveButtonText());
								deletebutton.appendChild(deletebuttontext);
								//SET UP THE CLICK EVENT OF THE DELETE BUTTON
								$(deletebutton).click(function () {
									var myliveinstances = control._source.instances.slice();
									// FIND THE ROW THAT CONTAINS THE DELETED INSTANCE
									var found = myliveinstances.indexOf(event.target.parentElement.getAttribute("data-instancename"));
									control.removeRow(found);
 
									drawrows();
								});
								rowdiv.appendChild(deletebutton);

Delete the Right Row

The delete row feature needs to find the row in the live set of instances (the _source.instances) using the HTML element that is the parent of the button. So if you click the Button, which is on the third row of the HTML User Interface, then using the instance name we can find the correct row and remove it. This is because the control.removeRow() uses the zero-indexed array in control._source.instances, not the user interface you are building. For example, if you have four instances on screen, then you delete one, the HTML will show that instance 1, instance 3 and instance 4 remain for example. But the Array members are now marked as rows 0,1 and 2.

The code closes out with the standard stuff :

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
update: function (el) {$("#errordiv").empty();	drawrows();},
				unmount: function (el) {
 
					if (control.getProperty("name") == "xEntity") {
 
						$("#entitycollector").remove();
					}
				}
			}
		}
	}
})
 
 

And so, what remains to be done? Well, of course, reorganize all that code written as a stream of consciousness!

But seriously, the obvious ones are

  • Ensure that when an instance is created, but the user has not yet entered any data, that the error checking is fired ( you can actually advance to the next page and come back to see the errors, because of the way the error object is handled only on the mount , not on the update.
  • Ensure that errors are removed from the page when they relate to an instance that was deleted by the user.
  • Ensure that the HTML building includes CSS class information to facilitate a new look
  • Build out date-time input and currency input and saving

But I’m sure you will find time for that. Have a nice day and enjoy building your own Custom Entity Collect Extension in Oracle Policy Automation!

We’ll leave you with a short video.