JavaScript Extensions working together in Oracle Intelligent Advisor

The following example is designed to demonstrate, in a simple-to-understand way, the concepts that surround JavaScript Extensions working together in Oracle Intelligent Advisor. Questions about these concepts regularly come up and hopefully this will be a useful article to describe some key concepts within the JavaScript Extensions. What will we talk about?

  • The different object keys of control extensions
  • The different methods of various control types
  • The Update object key

The different object keys

As most of you will know, when writing JavaScript Extensions for Oracle Intelligent Advisor, different keys are available for us to write code in, which correspond roughly to common events in the lifecycle of the control:

OraclePolicyAutomation.AddExtension({
	customInput: function (control, interview) {
		if (control.getProperty("name") == "xMyExtension") {
			return {
				mount: function (el) {},
				update: function (el) {},
				validate: function (el) {},
				unmount: function (el) {}
			}
		}
	}
})

In the above example you can clearly see the four keys provided with a custom Input control : mount, update, validate and unmount. The majority of control extensions have these keys, and some have other keys related to specific things that they need to function properly. But these four are omnipresent for most controls that actually have some degree of interaction with the end user. Mount and unmount are fairly obvious – they represent the initialization (of your UX, of your object) and the destruction of the same. The validate key is used to validate the data entered by the user and allows the developer to display error messages and so on.

JavaScript Extensions working together – the update key

Which leaves the update key. Contrary to what maybe you think it is for, it does not represent an event related to your control – at least not always, and not directly. The update key is called (and your code executed) very often indeed. To quote the documentation “Invoked when anything in the interview has changed. Has the parent DOM element passed as a parameter.

We’ve been covering JavaScript Extensions for years and we even have a code generator to help you make your own code, but the subject of communication between components is something that comes up quite often so we thought it would make a good article. And this concerns the update key. In Oracle Intelligent Advisor, a developer often has a conceptual challenge understanding how controls communicate. Developers have lots of experience building user interfaces, designing event-driven code to respond to clicks, drags and so on. When confronted with Oracle Intelligent Advisor, the following things become clear:

JavaScript Extensions working together

  • There isn’t a rich, control-based event model. That is to say, an Input Control Extension does not have access to events like click, drag, blur etc. They only occur on the physical layer of the browser.
  • The control has only the object keys as a mechanism for us to write code to respond to what the control is doing. Mount, Unmount and any others you choose to implement.
  • The control type directly affects the methods that are available to the developer (customInput, customLabel and so on). So to (for example) search for a global attribute and then add it as an entity instance, you need to have access to the control methods for the input extension and you need another control to have access to the methods specific to entity collects.
  • Thus, JavaScript Extensions working together becomes a bit of a challenge at first because they do not appear to have much capability to “see” each other nor “respond” to each other’s events.

Let’s look at the principles that might help you navigate this challenge. We’ll use a simple example of a Search extension that uses a REST API to let the user find something useful. And then when they have found it, they want to add it as an instance of an entity. And they want to be able to keep doing that. So here is the idea:

As you can see we can have two (or more) JavaScript Extensions working together to create a seamless experience for the user. They search for something using a global attribute, then when they find each item, it is added to an entity instance list below.

Before we begin to look at the way these two work together, we need to look one more time at the update key. Here is the same code with a console log to show you how often this is executed on the entity collect:

JavaScript Extensions working together - Update Key Frenzy!

As you can see while we are selecting a value on the attribute or just generally “doing stuff”, the update is fired many times. So we have a mechanism that we can exploit – the search triggers the update on the entity collect (and any other extensions that you may have on the same Screen). There is a frenzy of updates. So you need a mechanism to avoid this triggering your code far too many times.

To keep this example simple, we have added an attribute “flag” that can have either a value of 1 or 0 – and this can be used to stop the code running when you do not need it. For example, here is the Search extension:

window.jsonoutput = "";
OraclePolicyAutomation.AddExtension({
	customSearch: function (control, interview) {
		if (control.getProperty("name") == "xRailway") {
			return {
				search: function (text, callback) {
					// console.log("Starting Search");
					var root = "http://transportapi.com/v3/uk/places.json?type=train_station&app_id=6177cc50&app_key=1bc1a0cd099973c085d3dd5aa64dec33&query=";
					$.ajax({
						type: "GET",
						beforeSend: function (request) {
							request.setRequestHeader("Accept", "application/json");
						},
						url: root + text,
						method: "GET",
						dataType: 'json'
					}).then(function (data) {
						for (var key in data.member) {
							if (data.member.hasOwnProperty(key)) {
								var val = data.member[key];
								val.text = val.name;
							}
						}
						var arr = $.map(data.member, function (el) {
								return el
							});
							
						callback(arr);
					})
				},
				commit: function (value) {
					if (value != null) {
						// this just sets the global attribute
						interview.setInputValue("station_name", value.station_code);
						
						// Polyfill for IE11 Object.entries
						if (!Object.entries)
							Object.entries = function (obj) {
								var ownProps = Object.keys(obj),
								i = ownProps.length,
								resArray = new Array(i); 
								while (i--)
									resArray[i] = [ownProps[i], obj[ownProps[i]]];
								return resArray;
							};
						jsonoutput = Object.entries(value);
						interview.getExtensionData();
						// let the update on the entity collect do something
						interview.setInputValue("flag", 1);
					}
				},
			}
		}
	}
});

Note for simplicity that the search sets the flag to a value of one, whenever the user selects a value from the search box. This is just an example as there are other ways to manage something like this, and it could be applied to other control type keys like validate for example on Input Extensions.

Now to see JavaScript Extensions working together, here is the entity collect extension. Note that this does not have any User Interface at all, it simply uses the available objects and methods to add rows to the control each time a value is selected by the end user. You will also see the “flag” is intercepted and reset to zero after running the code.

OraclePolicyAutomation.AddExtension({
	customEntityCollect: function (control, interview) {
		if (control.getProperty("name") == "xEntity") {
			return {
				mount: function (el) {},
				update: function (el) {
						// get length of rows array
					var numEntities = control.getRows().length;
					var mycurrentrecords;
					interview.getExtensionData();
         // Add a row if the flag indicates a value has been selected in the search
					if (interview.getValue("flag") == 1) {
						// add a row
						control.addNewRow();
						mycurrentrecords = control.getRows();
           // push values into the row
						mycurrentrecords[numEntities][1].setValue(jsonoutput[1][1]);
						interview.setInputValue("flag", 0);
						interview.saveData();
					}
				},
				unmount: function (el) {}
			}
		}
	}
})

The code is executed only when the flag is set, and then the flag is reset to zero to avoid further executions. Note also for simplicity in this example, the selected value from the Search is stored in a variable jsonoutput – it could of course be in a cookie, a piece of storage in the browser or something else. This is just for the example. Finally, the screen also includes a completely standard entity container to display some data to ensure the user can actually understand what has been created.

Learning to leverage the update key is part of JavaScript Extensions working together. Have fun!

Richard Napier

Author: Richard Napier

After 8 years in case management and ERP software roles, Richard Napier joined Siebel Systems in 1999 and took up the role of managing the nascent Siebel University in Southern Europe. He subsequently was Director of Business Development and Education for InFact Group (now part of Business & Decisions) for 8 years. He now runs Intelligent Advisor IT Consulting OÜ. Owner of intelligent-advisor.com, he also is Co-Founder of the Siebel Hub.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Intelligent Advisor IT Consulting Serving Customers Worldwide
Hide picture