CHAPTER 11

image

jQuery with APEX

by Tom Petrus

What is jQuery, and what does it mean for you in APEX? Basically, jQuery is a JavaScript library that was made with cross-browser compatibility in mind. It is meant to enhance the life of web developers who use JavaScript by making them able to actually develop more instead of spending time on implementing all sorts of hacks and tricks to make said developments work. This is especially true for several Internet Explorer (IE) iterations; specifically, IE 6, IE 7, and IE 8 are all dreadful in this regard because those versions especially often did not adhere to web standards, made own implementations, or just did something special like compatibility view.

jQuery makes an effort to implement various fixes and workarounds when problematic implementations are present in a browser (or its implementation). But more so than providing cross-browser safety, it is just as much a tool of convenience and improves the quality of life of web developers seeking to enhance their JavaScript tool set. Selecting elements, for example, has been made easy, manipulations of the DOM are done just as quick, and it has a solid AJAX interface.

The importance of jQuery in APEX has been increasing ever since it was bundled with APEX. It is not always visible, but many libraries, functions, and widgets have been making the move to jQuery implementations as APEX has progressed. Interactive reports are one example. In releases prior to 5, you were limited to one report, but now you can have multiple reports on one page. With that change came a shift in the JavaScript code used for interactive reports because the JavaScript code until then was targeted at only one interactive report, and it is now a jQuery UI widget. This goes to show that jQuery has been here for a while, and it is set to be with us for quite a while longer.

This chapter will take you through some of the basics of what jQuery is and how to employ it. While some of the coverage is general, many examples are seated in the Application Express atmosphere, selecting elements and performing manipulations on them. You’ll see how to traverse the DOM starting from an element and how to deal with assigning and listening to events. Finally, you’ll learn how AJAX calls are performed through jQuery and how this can be employed through APEX’s API.

JavaScript and jQuery

There can be no chapter on jQuery without acknowledging JavaScript. After all, jQuery is a JavaScript library, and thus it remains important to have at least some basic knowledge of JavaScript. Having said that, this will not be an introductory course on JavaScript. But if you are serious about gaining some jQuery skills and want to interact more and more with your pages through JavaScript or jQuery, it is a good idea to learn about everything JavaScript offers.

Basic JavaScript knowledge encompasses things such as the following:

  • The data types
  • Equality and type coercion (== and ===)
  • Scopes (functions)
  • Closures
  • Prototypes

Enhancing your proficiency with JavaScript will automatically enhance your jQuery proficiency because ultimately they’re one and the same.

Basics of jQuery

This section will cover some of the more basic functionality of jQuery, including what it is and how it can be employed. You’ll learn how to select elements and how to manipulate their attributes and properties.

The jQuery Object

To use jQuery in APEX, you do not need to do anything special. There is no additional JavaScript file you need to load somewhere. Since jQuery is so much at the core of APEX, this library is always present and thus allows you to use jQuery right from the start. Plainly put, jQuery exists on the page as two objects: the variables $ and jQuery. The $ variable is just a convenience shorthand reference to jQuery, which is a function that will return something when passed something such as a selector (though it is important to mention that selectors are not the only thing it can do something with).

Selecting Elements

Learning the basis of working with jQuery starts with selecting elements on the page to interact with. In short, jQuery utilizes CSS selectors and has extended that functionality with some of its own (pseudo)selectors. Selectors are passed in as the first argument in the jQuery function. Listing 11-1 shows a couple of selectors, and if you’ve written or seen CSS classes before, you’ll be quickly familiar with how jQuery selects.

If these selectors are alien to you, I’ll explain some of the more common ones here.

Selecting by ID

First is the ID selector #, which is used to target an element on the page through its id property. IDs are expected to be unique throughout a page, and thus an ID selector (usually) means you will expect a single item. In APEX, each page item you define will have its id attribute set with the item’s name. Listing 11-2 shows a condensed version of the P6_ENAME page item’s generated HTML. Knowing the name and the selector syntax results in the selector #P6_ENAME.

Compare this easy-to-use selector for jQuery’s method of selecting an element through an ID with the usual way of doing so in standard JavaScript with the DOM API, shown here:

document.getElementById("P6_ENAME")

Selecting by Class

The second selector you will encounter a lot is the class selector . (a dot, yes). The class selector will search elements through the class assigned to them. Elements can have no, one, or more classes assigned to them, and thus a class selector usually means you expect to select a collection of elements. Listing 11-3 has two input elements in it that have different IDs, and each has two classes assigned. But they both share a class, myClass.

Both elements can be selected simply by using this class name and the class selector, with a resulting selector of .myClass.

Another example is where you assign a class to a column link so you can intercept it for a dynamic action.

Selecting by Attribute

Another important selector is the attribute selector [], by which you can select elements through a specific attribute and even a value. This selector is often used in APEX to select a collection of elements in classic reports or tabular forms since APEX assigns the header attribute with the column’s alias from the query to each element. Listing 11-4 shows a simplified version of this structure generated in classic reports and tabular forms.

The reason for applying this headers attribute to cells is that while selecting rows in tables is rather trivial, there are no methods to select columns. For example, say you have a classic report based on table EMP, which has among its columns the column ENAME. Selecting all the elements for this column would be achieved by using [headers="ENAME"] as a selector. Note that quotes are not necessary as long as the value is a valid identifier. It is also important to remember that selectors are also wrapped in quotes, and thus quotes will have to be stacked or escaped correctly when using them.

Hierarchy

Sometimes it is necessary to define a certain path to reach a certain selection. For example, an APEX page is defined with two regions on it, and each region contains two page items, which all have a same class assigned. Selecting the page items in the second region by only using the class selector requires the ability to narrow down the selection’s path. And this can be achieved (and again, I’d like to iterate that this is the same as for defining CSS classes!) by specifying additional selectors in the same string. Listing 11-5 shows a simplified example of two regions with two input elements each. Both div elements have a different ID, and both are part of the body element. The input elements all have the class myClass assigned to them.

Selectors go from left to right, and this compares to narrowing down the search in the DOM from wide to narrow. It is easy to get all the input elements by simply using the node name as a selector $("input"), or in this specific example they can also be retrieved by using their assigned class $(".myClass"). It’s important to note that if, for example, the div elements also had the myClass class, it would be possible to combine the previous statements to only target input elements with the assigned class $("input.myClass"). This is specifying on one level of selection but wouldn’t solve targeting only the items in the second region.

And thus a path has to be made. This can be done by selecting the correct region first and then provide further selectors, and this can be read as first selecting one element and then within that element’s context look for the existence of elements with further selectors. Selecting the second region is easy enough since the ID is plain to see: $("#region2") would return that div element. To get to the input elements within that element, I add a further selector, $("#region2 input.myClass"), and now only those two input elements would be retrieved. Note the space between both selectors and how there is no space between input and .myClass.

This sort of selection is especially handy to target elements in different regions or reports.

Image Note  Selectors as an argument to the jQuery function are to be passed as a string. This means you will have to put the selector in quotes as you pass it along!

DOM Nodes and jQuery Objects

When working with jQuery and making selections and alterations, you’re actually manipulating the Document Object Model (DOM). This model is an API to interact with the page in the browser and has been built to resemble the page’s HTML structure.

The DOM API is what you use when you, for example, use document.getElementById. This will return a DOM node, such as the input element with the ID P6_ENAME. This node can then be further altered by the functionality provided by the DOM API on this particular type of node. However, some problems start to arise when different browsers have different implementations or levels of support for certain features. For example, the property .innerHTML on nodes may not work properly across browsers.

This is where jQuery alleviates some of these more problematic implementations. When selecting elements through jQuery, there is of course also a DOM node, but it is held in a jQuery object. You can still easily access that DOM node and manipulate it with the API provided in the DOM, though that is not really the point because you really want to use the functionality jQuery layers over that direct access.

Too often selections between DOM nodes and jQuery objects are mixed up or not used with enough thought. It might be fine to use the DOM API without fear, but this really does require you to know what browsers you have to support and whether they support it correctly. As a rule of thumb, I simply always use jQuery to do what I want simply out of convenience and uniformity. It is up to you as a developer to have a critic’s eye on this when you write or copy JavaScript code from anywhere!

Determining and Testing Selectors

Now that some basics have been established, it is time to look at how to employ this knowledge. To do this, you’ll use the browser’s developer tools. Personally, I strongly prefer using Chrome’s or Firefox with the Firebug plugin over IE’s tools. The developer tools offer a view on the document’s elements, all loaded sources, network traffic, a console command interface, and others. By using these tools to inspect elements and what they are made up of and running commands from the console, you can determine and test all your code before having to save it somewhere in an APEX page or other location. This is a huge boon to developers, and you should check your selectors and code before placing that code into a component. Oftentimes I too need to fine-tune my selections or double-check selectors I’ve set up.

The developer tools can usually be opened in a Windows-based browser by pressing the F12 key, but are also accessible by going through the options menus. Figure 11-1 shows the developer tools opened in Chrome, with the elements panel opened. The page is a form page on the EMP table.

9781484204856_Fig11-01.jpg

Figure 11-1. Chrome’s developer tools opened with the elements panel active

Usually at the top of the elements panel you can find a magnifying-glass icon. This icon will allow you inspect the elements on the page itself. Clicking it will activate it, and you can see element specifics as you hover over them on the page, while the selection will be highlighted in the elements panel once the element has actually been clicked. Figure 11-2 shows what happens in the elements panel once element P7_ENAME has been selected because it will open the structure to that element.

9781484204856_Fig11-02.jpg

Figure 11-2. P7_ENAME selected in the elements panel

Selecting elements on the page and navigating through the elements panel is a great help when needing to determine selectors. While usually it is logical to determine a specific item’s name because it has also been defined in the Page Designer, it becomes a bit harder when selecting generic regions or collections of items.

So now I’ve determined the selector for item P7_ENAME, namely, #P7_ENAME, and I can test this selector by using jQuery in the developer tools’ console window. In this console window JavaScript can be run on the current page, which is extremely helpful and powerful. Since jQuery is also present on the page, it is possible to use jQuery statements here as well. Again, you don’t need any special initialization for using jQuery on your pages!

In Figure 11-3, the console panel has been opened, and I’ve entered and run my JavaScript statement. The console shows the output too: the return value of the code that has been run. jQuery returns the elements it has retrieved from the selector you gave as an argument.

9781484204856_Fig11-03.jpg

Figure 11-3. The console window opened with the selector executed in jQuery

Only one element was selected, and that is the input element you needed. So, now you have verified your selector and see that it works.

Basic Manipulations

Now that you understand selectors and how to execute them, it is time to explore what you can do with this selection. I mentioned before that jQuery’s power lays in cross-browser implementations and how it is better to use it over that of the DOM’s API, and here you’ll see some examples.

The most basic form of accessing attributes is, as you will usually see, through the DOM API. This can be the value, an onclick attribute, a class, a custom property, and so on. Most actions can be performed on the nodes themselves, but once again, it is important to note how some of those API implementation may differ between browsers. In modern browsers, the value property (mostly) works as you’d expect, but there may be slight differences, and in older browsers it may simply not do exactly what you’d expect.

Elements can have a whole realm of possible attributes, and some attributes are really just that, attributes, while others can be more special. An example is the class attribute, which actually holds a list of classes, or the style attribute, which defines inline CSS rules. Not each attribute is accessed in the same way, nor do they all have a similar meaning. This is reflected in jQuery, where different functions to access these various attributes are used.

<input type="text" id="P6_ENAME" class="myClass" value="Tom" />

value and text

Even though value is a property, it is different because of how it behaves. jQuery has a dedicated function for getting and setting the value of items: .val() . To retrieve the value of an item, just val() can be called: $("#P6_ENAME").val(). To set the value, an argument can be passed to this function: $("#P6_ENAME").val("Not Tom"). You should always use .val() to do this and not use the DOM’s .value API call. Even better would be to use APEX’s apex_item API, but I’ll cover more about that later.

Note that this function is used for input elements. Select lists are a different kind of element, and retrieving the value from a select list is completely different, and the same holds true for radio buttons. Also, be aware that APEX will generate HTML differently for check boxes in that it will not simply create an input element with the provided item name. This is why it is always important to double-check the HTML that has been generated so that you know what you need to select.

Don’t try to use .val on elements such as span or div. It has no use since they have no actual value. Sure, you see text in them, but that does not mean they have an actual value attribute. And this is a common mix-up when first starting out. When looking at the actual HTML of, for instance, a span element, it is easy to spot the difference with an input element: <span id="myText">This is text to be displayed!</span>. It should further be noted that this text is actually a child node of the span tag in the form of a text node, and even in the DOM API there is no direct access to the text from this element without descending the tree one step further. Thankfully, jQuery saves the day by providing the .text() function. This function once again allows you to get and set the text of a node: $("#myText").text("This text has been altered").

Attributes

Attributes are generally all the actual attributes you can see on an element and always consist of a key and a value. For example, Listing 11-6 shows a really basic input element with just two attributes, id and value, put in a table cell with an attribute of headers. That is the representation in HTML, however, and in that plain form all the attributes are plain attributes. In the DOM, however, many attributes behave differently from just being plain getters and setters.

In jQuery you can access all attributes on an element by using the .attr() function, providing the attribute’s name as an argument to the function. This is in contrast with directly accessing the node’s attributes through the DOM API. To retrieve the value of the headers attribute in Listing 6-6 or the value of id on the input element, attr can be used just like this:

$("td").attr("headers")
$("input").attr("id")

Boolean Properties

Some attributes on elements behave as properties in that they indicate a binary state. This is something that follows the HTML specification but has seen problematic implementation cross-browser for a long time. Some examples of these properties are as follows:

  • readonly: Indicating whether the element should not be editable but still post its value when the form is submitted
  • disabled: Indicating whether the element should not be editable and not post its value when the form is submitted
  • checked: Indicating the state of the check box on a check box input item

Often you’ll see these attributes added on elements in a manner that actually doesn’t conform with the HTML specification. For example, the simple presence of the readonly attribute is enough to indicate that the input element is in a readonly state. It doesn’t need further specifications such as is often seen.

  • readonly="readonly"
  • readonly="true"

These states exist because of how different browser used to react to these properties. Nowadays these problems don’t exist as much anymore, but they still linger on a bit. In jQuery these properties can easily be checked by using the .prop() function, which will return a true or false value.

Listing 11-7 shows an example of using the prop function. An element is selected, and then the readonly property is tested.

To actually set a Boolean property on an element, prop has to be used with two arguments: the name of the property and a Boolean value. The correct syntax will be put on the element.

prop doesn’t see much use overall. Plenty of people are still using .attr() to get and set the values of these properties, much in the way described earlier. But prop can be much more interesting when writing code since it will return a Boolean value, possibly reducing your code by not having to double-check the value of the attribute. For example, constructs like this end up being used: if ( $("#P6_ENAME").attr("readonly") === "readonly" ). Compare this with the ease of use of prop, and it should be easy to spot the difference. But be consistent in your use! Don’t use prop one time and readonly another time. Take care when adding HTML in the various places you can in APEX. You may very well have used readonly="false" somewhere, and even though that is wrong, browsers are forgiving. This will mess with prop, however, because the simple existence of the readonly attribute counts as true.

Classes

Classes on elements are again a different type of property. A more apt naming for this attribute is classlist because this attribute holds a list of currently assigned classes to the element. It could be one, and it could be more. Using classes has multiple uses: they are used to assign style rules on them through CSS classes, and in JavaScript they are used to make selection easy, group elements, and track certain changes.

A common example of using a class to group a set of elements to make their selection easier is assigning a class to the column link of a report. Doing so will make all the generated links easily identifiable from JavaScript to assign, for example, a dynamic action to them.

The other common use in JavaScript is to toggle classes on elements to reflect a certain change done on them. A class could be assigned to items affected by a piece of code, which in turn makes identifying these changed items easy because they can easily be selected again.

And because classes behave more like an array of assigned classes than just a singular value, it has been reflected in jQuery as such. Adding, removing, or toggling a class has been made easy.

  • addClass: Adds a class (or classes!) to an element and will not generate a duplicate entry. Here’s an example: $("#P6_ENAME").addClass("trackingClass").
  • removeClass: Removes the specified class(es) from the elements. Here’s an example: $("#P6_ENAME").addClass("trackingClass").
  • toggleClass: Adds or removes a class as necessary. For example, $("#P6_ENAME").toggleClass("trackingClass") will add trackingClass when it isn’t present or remove it when it is.

Iterating Over a Selection of Elements

Oftentimes a selector will not target just one element on the page but rather a collection of them. This is especially the case when working with broader selections such as classes or node types. And often enough these elements have to be dealt with in an individual manner instead of with one sweeping all-altering statement. jQuery has several ways to deal with this, such as filtering a selection further down or allowing you to iterate over each element selected.

An iteration can be achieved by using the .each() function, which will execute a JavaScript function for each element in the selection and provide the current element in the set as the context.

In Listing 11-8, an HTML snippet holds a table with two rows. Both rows have two cells; one holds the ENAME, and the other holds the SAL value of an employee (employee name and salary, respectively). It is a condensed example of a tabular form. To target and manipulate the elements containing the salary, you’ll employ the techniques shown so far and combine it in each statement.

Keep in mind this is a simplified example, and it is perfectly possible just to select input elements. In practice, you will quickly see this dashed; on an APEX page, there are plenty and plenty of input elements! So, the goal here is to show a selector that makes sense.

Image Tip  If you’d like to follow the example selectors and code throughout, creating an APEX page with a tabular form on the EMP table will work absolutely fine! Provide a static ID to the region with the ID emp-tabular. The ENAME column is a display-only column.

To get those input elements, one way would be to go through the cells since they have a headers attribute. $("td[headers=SAL]") would achieve targeting the cells, while with the addition of the input selector, the input elements in those cells can be targeted with $("td[headers=SAL] input"). Running this statement will return an array with two elements, namely, those two input elements.

The code in Listing 11-9 will select those elements and apply the each function on this selection. The statement will print out the value of each element as it processes them. The each function takes a function as an argument, and thus an anonymous function is passed in. This function is then executed for each element, and jQuery will set the value of the context, the this object, to the DOM element of the current element in the each loop. This allows easy access to that node and thus allows an easy way to fetch the value each time.

The console.log function is a function that will print out lines to the developer tools’ console window and thus allows for easy debugging. This is once again a great way to test and prototype code.

Image Tip  Take note that the this object is the DOM object and not a jQuery element. To leverage jQuery’s value-accessing function, you need to create a jQuery object of this DOM element.

Advanced Uses of jQuery

This section will go a little deeper into some jQuery functionality than simply selecting elements and making basic manipulations. Most important, you will learn how to deal with events in various ways and how to travel up and down the DOM starting from a selection. You can then use chaining to make your code more concise, and finally you will see how to employ the power of AJAX calls.

Handling Events

Another common practice made easy with jQuery is attaching event handlers on elements. Examples of events are mouse clicks, cursor events such as entering and exiting an element, key presses, browser events such as a window resize, or even custom events. Handlers are attached to elements, and when the event triggers, the handler (or handlers) is resolved. This “metadata” way of handling events through events and listeners has been good practice for a while now. However, still a lot of code can be found that incorporates assigning handlers directly to the DOM nodes’ attributes or in HTML.

Assigning Through Attributes

One such attribute is the onclick attribute, which is commonly found in forum posts or examples. I’m not a fan of it, and here is why: it’s inconsistent, and it clashes with the event-handling system. Furthermore, adding this onclick attribute adds JavaScript code in what is supposed to be just HTML. It’s usually good form to try to separate concerns: HTML represents the data, CSS applies style, and JavaScript applies dynamism. A reason frequently seen to use this attribute is for the sake of convenience or ease. It’s tempting to provide this attribute in a property on an APEX item or a column link or adding it along in an apex_item call because it’s easy to add some value to the function called. But nowadays this way is simply not regarded as being clean.

Assigning Through Convenience Functions

In jQuery, events are generally pretty easy to handle, especially basic ones such as a mouse click or acting on the change of an item’s value. For instance, dealing with a change event is as easy as calling the change() function on a jQuery object.

$("#P7_SAL").change( function() {
  console.log( "My value has changed to " + $(this).val() );
} );

Calling the change() function requires one argument: a function to be used as a callback for the event. And once again, just like with each, the object this has been set to the element that triggered the event.

Just like the previous change event, there are many other convenience functions to quickly bind handlers to selections: click, focus, mouseover, and so on.

Assigning with on

The convenience functions are shorthand for commonly used handlers and actually call one of the core event-handling functions in jQuery. The two core functions are the .on() and .off() functions. Attaching the same change handler as done earlier with .change() can be done with on().

$("#P7_SAL").on("change", function() {
  console.log( "My value has changed to " + $(this).val() );
} );

Note that with this event handling, multiple handlers can be bound to the same element and to the same event. Running the previous two commands will simply double up the output in the console log.

Think back to the code in Listing 8-8 with the tabular form example. Selecting the input elements in it and applying an event handler to this selection will bind a handler to each element in that selection. In Listing 11-10, a change handler is assigned to each input element, and in the handler, the changed value is shown, as well as the current context. Note how an object can be passed to the console output, which is also shown in Figure 11-4. Providing an object as an argument to the console will make the console put out that object and allows inspecting its properties. If a DOM node is provided, it is even possible to see this element in the web page or jump to the elements panel, which is again a wonderful tool to help out with developing code.

9781484204856_Fig11-04.jpg

Figure 11-4. Console output for an object

Image Note  When putting out an object in the console, take care not to concatenate it with text. This will implicitly call the toString() function of said object, resulting in an output you can’t do anything with.

Dealing with Events on Dynamic Elements

There is a caveat, though. Assigning handlers through the shorthand or in the way shown earlier will create static handlers. These handlers are attached to the elements that were in the selection at the time of assignment. If elements that match the selector at a later point in time are added to the page (actually, the DOM), it is normal that no handler is assigned automatically (that’s why it’s called static). An example is the change event on elements in a tabular form, where the tabular form has pagination. When pagination occurs, the current elements are discarded from the DOM, and a new set is entered. Thus, new elements are added to the DOM; these are elements that were not there when the event handlers were first assigned. Another example is the column link on an interactive report with a class defined in its attributes. If a handler is assigned on those eligible elements on the initial page load, those handlers would be gone when the report refreshes or is paginated because those handler’s elements are then removed from the DOM.

There is, of course, a way to have more dynamic handling of events. First, it is necessary to understand just a bit more of exactly how an event behaves. Events happen all the time; they don’t just occur when a handler has been bound. They’re also not stuck in the location they are fired from. Events will travel from the element they are fired from all the way to the top of the DOM tree. This behavior is called bubbling or propagation.

Thinking back once again to the tabular form example code and the change handler assigned on its input elements, this means that if a change handler is also assigned to the enclosing table, the output in the console will reflect this.

$("#emp-tabular, td[headers=SAL] input").on("change", function() {
  console.log( "Change event on: " );
  console.log( this );
} );

When changing an input element of the salary inputs, four lines of output will be produced. The two objects logged will be the element the change has taken place on and the table. This is to illustrate how the event has actually traveled up the tree and how it doesn’t simply stop at the element it fired on!

Image Tip  I’ve combined two selectors previously to expand the selection made by using a comma. Selectors separated by commas will simply expand the selection and not influence each other.

Now consider how once again static handlers have been assigned. But this time, if the tabular form were to be paginated and new input elements were added, the handler on the table would still be there, and it would still be triggered when one of the new input elements is changed (not just salary!) because the event bubbles up.

This event propagation can thus be used to account for changes in the DOM after a handler has been bound to a certain element. Certainly, a handler will have to be static at some element in the DOM to be able to intercept an event, but it is not limited to just those target elements.

Here is where the on() function offers the required functionality. Not only does it allow binding to one static set of elements, it allows binding to an element to listen for an event specifically fired on a set of children matching a selector. For example, it is possible to bind the change handler on the table but listen only for change events on the input elements for cells in the salary column.

$("#emp-tabular").on("change", "td[headers=SAL] input", function(e){
  console.log(this);
});

The second argument to the on function is another selector here. In the handler, the current object is once again logged to the console. And now the change event will always be able to be handled even when the report is paginated.

Removing Handlers

Adding handlers is pretty easy, and so is removing them. But do note that only handlers that have been added by using .on() can be removed. Simply use .off() to remove a handler, but pay attention that the specifications have to match those used for adding it through on().

Stopping Events

Stopping events is not the same as removing handlers. In APEX there is a dynamic action that will cancel an event, and this stops the event from being handled. You’ll often see the statement return false; in online resources about APEX.

The difference is subtle but important. When you know how events work and that they bubble up in the DOM, you have different ways to stop an event. It is possible to handle the event and stop the event from bubbling further up the DOM or to handle the event and even stop other handlers on the same event from firing. Interacting with the event in an event handler is not hard. In the handlers shown in the previous example, a function was always passed in as an argument. jQuery actually passes in arguments to this function so that they can be interacted with if required. The first argument is the event object, which is a jQuery object containing information and interaction on the current event. Taking the change handler that is assigned to both the table and the input elements again, it is possible to make this event stop from bubbling up after the change event handlers have been executed on the input element.

$("#emp-tabular, td[headers=SAL] input").on("change", function(event) {
  console.log( "Change event on: " );
  console.log( this );
  event.stopPropagation();
} );

So, even though a change handler is bound to the table, no change event will be allowed to bubble up from the salary input elements to any elements above it. And in this example, no change event will propagate further up from any element in the table above the table since this event is once again stopped from bubbling there.

Another thing of note are default events. For example, the anchor tags <a href="#> have a default click handler because when the element is clicked, the browser will redirect to the URL specified in the href attribute. So, usually in examples on APEX that involve setting up column links and attaching an event handler to these elements, be it through dynamic actions or through handlers in jQuery, this default event is not dealt with correctly. The dynamic action “Cancel event” is correct; returning false in code is not. Assigning onclick="return false;" to the link element is also incorrect!

The correct method to stop a default event from executing its default action is by using preventDefault.

$("a.myColumnLinkClass").click(function(){
  alert("you clicked this link!");
  event.preventDefault();
});

Reacting on the Page Load Event

There is one specific event I’d like to highlight, and that is the page load event. This event is triggered when the page has finished loading. All content and scripts have been loaded, parsed, and rendered. Oftentimes it is necessary to wait for all this to happen before executing a block of code. For example, selecting a range of elements may not return the full set of expected elements when run before the page has finished loading since more elements may still be added to the DOM afterward depending on where the code is being ran. Another example is a script not having been loaded yet. Nowadays the trend is to place scripts near the bottom of the page simply because this is a better user experience. The DOM will first load the data structure and render this and then get to the script tags, loading those. Certain libraries or scripts added may thus end up getting loaded later.

Much depends on using page templates and just exactly where you will want to add code. But overall the load event will be something you want to wait for. In APEX there are several ways already to do this. Using dynamic actions, there is the page load event, an action itself can be set to trigger on page load, and not least of all the “Execute on page load” section on a page. These are all good places to put code, if not the best.

It may be, however, that some dynamic code is required to interact with this event or required simply because code has been put in a JavaScript file. This load event has a specific handler in jQuery that can be used: the ready() function. This function will wait for the DOM to be ready before executing the code here. There is also a load function, but the differences are subtle. This ready() function is also the function used by APEX in the various places mentioned earlier.

The ready() function has to always be bound to the document element and takes a function as an argument.

$( document ).ready(function(){
  //here comes the code you wish to perform on page load
  console.log("Page is ready!");
})

APEX’s Refresh

As an example for custom events, APEX has the perfect example. Whenever APEX refreshes an element, it will fire two events at specific times, for example when an interactive report is refreshed.

Right before the region is refreshed, an event apexbeforerefresh is triggered on the region, and when the refreshing has finished the event, apexafterrefresh is triggered. Binding to a region is easiest when providing the region with a static ID since this makes selecting it through jQuery easier. For example, with an interactive report region with a static ID set to myStaticRegionId, it is possible to bind to the afterrefresh event.

$("#myStaticRegionId").on("apexafterrefresh", function(){
  console.log("Report has finished refreshing!");
});

Traversing the DOM

The DOM can be seen as a big tree, with the document as the root and all other nodes under it. Selecting elements will then return matching nodes from this tree. But sometimes it is necessary to retrieve information from nodes that are children, siblings, or parents.

Note again how selecting elements with selectors is not hard at all. This is always top to bottom, from the root to a set of elements. It is even possible to select elements within a given context. This goes to show how it is relatively easy to narrow down a set from parent to children.

However, the same can’t be done from bottom to top. Selectors don’t do this. Fortunately, jQuery has many handy functions that allow traversing the tree structure of the DOM.

Going Up

Look back at the code in Listing 8-8 with its tabular form structure and the selector used to fetch the salary inputs. It’s possible in that listing to retrieve the row element for each input element. There are several methods to do this, though usually the requirement would be to have as flexible code as possible to deal with markup. For instance, it is obvious from the snippet that the input element is nested in a td element, and this element in turn is nested in a tr element. For direct traversal upward, parent() could be used, and this will return the direct parent node of the selected elements. Thus, it could be argued that calling this function twice on the selected input elements will return the row element, and this would be correct. However, a simple template change could completely destroy this kind of code. Therefore, it is more interesting to use the closest() function. This function will traverse up the tree, starting at the origin element, until it found an element matching the selector provided as an argument. This is great because it allows a lookup otherwise not possible.

In Listing 11-11. each input element is retrieved, and for each element the parent row is retrieved and put out in the console. This combines several of the techniques already shown.

Going Down

It is also possible to easily descend a node’s structure, which can be regarded as searching in a certain context. The function find() accepts a selector as an argument and will look for elements matching this selector within the context of the given jQuery object.

So, going further with the example from Listing 11-11, which finds the given parent node for the current element, it is possible to once again search for a certain node within the context of this given row. This opens up so many possibilities when working with reports or tabular forms in APEX! In Listing 11-12 the code from the previous listing has been expanded to not only retrieve the parent row but also find the cell with ENAME in it and print its text.

Note how the word context has already been used in several places, especially with find(). So far, I’ve always used jQuery’s invocation with one argument, a selector or a DOM node. It is, however, possible to provide a second argument, which when combined with a selector or DOM node will represent the context for the first argument. So, instead of using find() to get the matching nodes within myRow, it is the same as using myRow as the second argument in a call to jQuery with the selector as the first argument.

var enameCell = $("td[headers=ENAME]", myRow);

Keeping It Level

Traversing doesn’t always mean traveling up or down the tree. It may mean finding elements on the same level as the current element. To this end, there is the siblings() function. This function also accepts a selector as an argument to filter the set of sibling nodes. So, instead of traveling up from the salary input element, it is possible to go up to the cell element and then filter that cell’s siblings to find the ENAME cell.

$("td[headers=SAL] input").each(function(){
  var myCell = $(this).closest("td") ;
  var enameCell = myCell.siblings("td[headers=ENAME]");
  var ename = enameCell.text();
  console.log( "The salary $"+ $(this).val() + " belongs to " + ename );
});

Chaining Functions

With all functionality available on jQuery objects, it can be troublesome to always store the output in a variable and then do something with that output. As discussed earlier with traversing, a jQuery function is called three times, and the output is always stored. This isn’t really necessary because jQuery allows chaining functions.

What this really means is that when a call is made to a jQuery function that returns a jQuery object, another function can immediately be called on that returned object; this is called chaining.

With this, it is possible to rewrite the code in Listing 12-12 instead of assigning to variables to chain the functions together.

$("td[headers=SAL] input").each(function(){
  var ename = $(this).closest("tr").find("td[headers=ENAME]").text();
  console.log( "The salary $"+ $(this).val() + " belongs to " + ename );
});

This can really help in keeping code concise and having to assign to variables all the time.

Chaining can go pretty far. The following snippet illustrates how chaining can be used and how the end() function can be used to return to the original selector:

$("#emp-tabular")
  .find("td[headers=ENAME] input")
  .each(function(){ console.log(’Ename:’ + $(this).val()); })
.end()
  .find("td[headers=SAL] input")
  .each(function(){ console.log(’Sal:’ + $(this).val()); })
.end()

This code will first select the element with the ID emp-tabular. The find() function is then used to find elements within that selection. Next, these elements are iterated over by using each(), and in which the value of the element is sent to the console output. Then end() is called, which will cause the next chained function to start again from the original selection, namely, the emp-tabular element. Again, a selection is made with find(), and each is used to loop and written to the console. A final end() call will cause the final returned element to be the emp-tabular element.

APEX JavaScript APIs

APEX has an own extensive set of JavaScript functions to deal with APEX-specific functionality on its pages. Over the years of APEX development and releases, much functionality has changed and been added. Most notable is the shift from working with DOM nodes to working with jQuery selectors and objects.

Overall, my advice is to use APEX functionality whenever possible. An example of this would be retrieving values in JavaScript from the different sorts of page items. The API apex.item() implements value retrieval from normal text items, select lists, check boxes, radio groups, and so on. Even though .val() has been explained here, .val() just does not hold the same value as apex.item.getValue() because the latter will work on any type of item. And similarly for setting values, apex.item.setValue() is a better option.

It is important to take note of the API documentation, though. Many functions of the API have the ability to take different sorts of input (similar to jQuery), though usually it is limited to a string, which may or may not be a selector or a DOM node. For example, apex.item() will take an ID for an item but not a selector. Creating an apex_item object for P7_ENAME will be in the form of apex.item("P7_ENAME") and not like apex.item("#P7_ENAME"). The reason here is likely to be that apex_item implicitly expects to target one and just one element and thus assumes the #.

AJAX Calls and AJAX Callback Processes

Another area jQuery has a strong implementation for is performing AJAX calls. The interface it provides is extensive and will safely work in all browsers. Furthermore, APEX has implemented an interface to call on-demand processes from JavaScript based on the jQuery AJAX suite, which means it’s interesting to look at. The documentation for the APEX implementation also mentions this; there are some presets and extensions, but all settings can still be provided and overridden.

First, the important thing to understand is what the first A in AJAX stands for: asynchronous. What it comes down to in short is that a function is called but will not return an answer right away. This has a big benefit for users because the browser is not locked up while waiting for the function to return a result.

As an example, think of APEX’s interactive report pagination. The next set of records will be fetched through an AJAX call and will then be added to the DOM, replacing the current set. A spinning icon will be shown throughout to indicate this process is occurring. Imagine this form of code:

function(){
  start_the_spinner;
  refresh_report;
  stop_the_spinner;
};

This is the most common mistake when first dealing with asynchronicity. It assumes that refresh_report is called and fully resolved before stopping the spinner. However, consider that refresh_report will actually initiate an AJAX call, asking the server for a reply (the new set of records), and when the server has returned these records, it replaces the nodes. If this happened synchronously and fetching the new rows took five seconds, the user would look at a locked-up screen, with a nonspinning spinner since the thread has no time to make it spin. But everything would occur “inline.”

With AJAX calls, you can provide a function to a success handler, which will execute the function when the call has completed. Thus, stop_the_spinner should be put in a success handler of the call to refresh_report.

On to jQuery’s AJAX implementation: if ever the need is there to get some data from an on-demand process, this can do it.

Setting Up an AJAX Callback Process

Listing 11-13 has some simple PL/SQL code to be used as an on-demand process. This code creates a new process named MY_CALLBACK on an APEX page under the AJAX Callback point.

This is a simplistic piece of code. It will concatenate the value of two variables and then return that string. The X01 and X02 variables are variables usable in requests made in APEX. They go from 1 to 10, and there are also global arrays, F##.

Using jQuery to Call a Process

Listing 11-14 shows how to call this process from JavaScript using jQuery’s ajax() function. Starting with the actual jQuery call, which is the last-to-previous line, jQuery’s ajax() function takes one argument, which is an object with all settings for the AJAX request to be made. This object is the variable ajaxSettings, which contains these properties:

  • url: wwv_flow.show is APEX’s show page procedure.
  • data: The data that needs to get sent in this request. This is another object and is configured before the ajaxSettings object.
  • type: The type of request to make. There are, for example, also GET requests, but I want to use a POST request here because I don’t want the server to cache the results of this call.
  • dataType: This defines the data type of the callback’s return.
  • success: This is the handler that will be executed when the call has completed. The function’s first argument is the return from the callback parsed according to the defined data type. In this code, the return is then logged to the console.

These settings are pretty usual and will cover most cases. The data type will usually differ, though, and usually this may be of the data type JSON because this is a common and universal data type used to exchange information and is natively accessible from JavaScript.

Passing data to a callback is done through the data property. This property is filled out through the ajaxData object.

  • p_request: This defines what has to be called. Here the application process MY_CALLBACK is to be called; this is the callback process defined earlier.
  • p_flow_id: This is the application ID. The value is retrievable from the page and is fetched through a shorthand for the apex.item().getValue() function.
  • p_flow_step_id: This is the page ID and is retrieved similar to p_flow_id. This parameter is required when a process is to be called that is defined on the page concerned. If left out, an application process can be used.
  • p_instance: This is the session ID and is retrieved similar to p_flow_id.
  • x01, x02,...: These represent a series of singular value parameters usable in these kind of calls. They’re great because this way no additional page items have to be created to use a callback process!
  • f01, f02,...: These accept an array with values (not used in this example).
  • p_arg_names, p_arg_values: There are two properties that accept an array of values. The former holds an array with page item names, while the latter holds an array with values for the specified items. Note that the matching of items with their values is done on position within the arrays. (This is not used in this example).

Image Tip  The code to perform the call has been wrapped in a function, and this is wrapped in parentheses, followed by (). This will execute the function anonymously, preventing variables from polluting the global scope.

Using APEX’s AJAX Functionality

APEX also has a strong implementation for making AJAX calls from JavaScript, implementing everything shown in the previous example and expanding on it with APEX-specific functionality , since it is nothing but a wrapper for jQuery.ajax(). In Listing 11-15, a call is made to the same callback process as in Listing 14-14. It’s much more concise since APEX implicitly adds some settings by itself. Most evident is the setting of the application, page, and session IDs. It requires just the process name. The second argument is the actual data object, with data that needs to be sent for use in the callback process. Instead of having to use the p_arg_names and p_arg_values arrays for items, APEX has provided the pageItems property, which allows you to specify selectors for items to retrieve their name and value from and which automatically adds these to the request.

The third argument is the settings object that will be passed to ajax(). Additionally, it allows you to easily show a waiting indicator.

jQuery Versions

Important to mention is the jQuery version that comes bundled with APEX. APEX versions 4.0 through 4.2 have been with use for quite a while, but unlike many improvements on the APEX side, the jQuery version has been the same for much of this time. This meant that the jQuery API has become quite outdated for a while now, and this began to show as more industrious members of the community started seeking out new and advanced functions, which brought along plenty other problems. So, the jump being made with APEX 5 is rather significant.

jQuery has been updated from version 1.7.1 to version 2.1.3.
jQueryUI has been updated from version 1.8.14 to version 1.10.4.

This is a big jump from versions 1.7 to 2.1...very big. Version 2.0 was a major release and cut out much of what was considered legacy functionality; examples were workarounds for browsers like IE 6. This shouldn’t be a surprise. Technology does not stand still, and at a certain point this kind of cleanup is necessary to keep the library as clean and lean as possible and provide a starting point for future implementations.

Be aware that if you have or had custom implementations in applications for versions previous to APEX 5, you may need to update the jQuery library! Or if you previously loaded an updated jQuery library version, it may not be necessary to do so anymore, speeding up your page.

apex.jQuery

Throughout this chapter I’ve been using $ and jQuery as the objects I perform jQuery actions with. However, there is also the apex.jQuery object. As shown earlier, APEX comes bundled with a certain version of jQuery, and as we progress in time, so will jQuery and APEX. At one point in the future we may end up at the same situation as we had with APEX 4.2 and its outdated jQuery library. It may become increasingly interesting to use another jQuery version than the bundled one, and then apex.jQuery comes in handy. Just as the global-scoped jQuery object will reference a certain jQuery version, so will apex.jQuery reference the jQuery version that APEX uses. After all, updating the library does not mean that all functionality will remain the same, and thus APEX always needs its own version.

This means that when you decide to use apex.jQuery instead of $ or jQuery, you will protect yourself from any adventures with other jQuery libraries loaded on the page. This may or may not be interesting depending on the situation, but generally it is a better idea to use apex.jQuery.

Pitfalls

Even though many things are easily resolved by using jQuery, don’t become complacent. Technology never stands still, and it is part of your job to keep up. Even though jQuery keeps evolving and has extended functionality does not mean you have to use it for every single thing.

An example is the native support of several functions on arrays in JavaScript, such as forEach. These native functions are usually inherently faster than jQuery’s implementation. There are even polyfills available to provide this functionality in browsers that do not support it yet.

In other words, it is wise to keep up with how JavaScript evolves.

Resources

Summary

With what you’ve learned in this chapter, you should be able to deal with at least some of the basic jQuery interactions on your APEX pages. There is, of course, much more you can do with jQuery, and this chapter was a primer to tickle your sense of adventure. By now, though, you should have a grasp of some of these basics, such as to be able to select a set of elements and perform an action on them like a manipulation, binding an event handler, or using it as a starting point for a DOM travel trip. With a short foray into AJAX, you should now have a lot of power in your hands once you begin to see the possibilities created by combining all these techniques. Anything is possible on your APEX page with the power of JavaScript and jQuery, though it will take mastering to temper oneself. Never forget to utilize the power of APEX too!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset