Tyler Muth’s Blog

Technology with a focus on Oracle, Application Express and Linux

Archive for the ‘JavaScript / AJAX’ Category

jQuery Custom Selector

Posted by Tyler Muth on March 23, 2011

I’m working on a little Google Chrome Extension in my spare time to add a little formatting and bells and whistles to AWR Reports like converting Physical/Logical Reads/Consistent gets and bytes to KB/MB/GB. Also adding sorting and filtering to key tables, as well as making all wait events “hot” with a popup to show there definition, etc. Anyway, I found myself writing the same logic in a jQuery selectors all throughout my code to find the section headings of the report. Essentially, they’re either a p, h2, or h3 that contain text, have no child elements, and are a first-child of body. So, I wrote a custom selector to encapsulate the logic and drastically reduce and simplify my code:

jQuery.expr[':'].topLevelTextElement = function(element, index) {
     // if there is only one child, and it is a text node (<div>hello world</div>, not <div><span>hello world</span></div>)
     if (element.childNodes.length == 1 && element.firstChild.nodeType == 3) {
		// if it's parent is the <body> tag, ie it's not nested several levels deep
		if(element.parentNode.nodeName == 'BODY'){
			return jQuery.trim(element.innerHTML).length > 0;
		}
     }
     return false;
};

Here are some examples of it in action:

// iterate over each element the selector finds and log what type of node (tag) it is.
$(':topLevelTextElement').each(function(){console.log($(this).get(0).nodeName)});
//console output:
//h1
//h2
//h2
//p
//...

// iterate over each h2 element the selector finds and log the text it contains
$('h2:topLevelTextElement').each(function(){console.log($(this).text())});
//console output:
//Main Report
//More RAC Statistics
//Wait Events Statistics

On a related note, if you know JavaScript, CSS, and HTML, writing a Google Chrome Extension is REALLY easy. The documentation and examples are still a little weak. Integrating jQuery, jQuery Plugins, and jQuery UI was also quite easy, but there’s a minor catch for image references in CSS in that relative paths don’t work (easily). You have to use your extension name as an absolute path. I’ll blog about this later. If you need it now, just ask…

Posted in JavaScript / AJAX, jQuery | Tagged: | Leave a Comment »

jQuery Autocomplete for APEX – Update 1.2

Posted by Tyler Muth on March 16, 2010

A while back I modified this outstanding  jQuery Autocomplete plugin to work with APEX. Lately there’s been a lot of interest in a jQuery autocomplete plugin on the APEX forum so I put together a few examples and cleaned up the code a bit. Here’s how it works:

  • When the page is loaded the plugin adds an event listener to one or more items specified by jQuery selector.  For example $(‘P1_SEARCH’).autocomplete… will enable autocomplete for the P1_SEARCH item.
  • As a user enters text into this item, the plugin sends the text back to an APEX Application Process as an http get request.
  • The Application Process queries a table using the text it receives in one or more query predicates.
  • The process sends back a pipe (|) delimited list of results via htp.p
  • The user can use one or more columns from the result to populate one or more items on the page.

Take a look at the APEX examples here.  You should also explore the original author’s examples here as he demonstrates a lot more functionality.  The APEX version is capable of producing any of those examples as well.

Update: As requested in the comments, I’ve added parameters x02 – x10 to allow extra page items to be passed back to the application process. I’ve updated page 2 and the following example code to show this.

Here is the JavaScript in the page header of the “Multiple Fields” example:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="#WORKSPACE_IMAGES#jquery.autocompleteApex1.1.js"></script>
<link rel="stylesheet" type="text/css" href="#WORKSPACE_IMAGES#jquery.autocomplete.css" />

<script type="text/javascript">

$(document).ready( function() {
    $("#P2_SEARCH").autocomplete('APEX', {
            apexProcess: 'EMPLOYEES_EXTENDED',
            width: 400,
            multiple: false,
            matchContains: true,
            cacheLength: 1,
            max: 100,
            delay: 150,
            minChars: 1,
            matchSubset: false,
            x02: 'foo',
            x03: $('#P2_DEPARTMENT').val()
        });
    // the following statement sends the result to multiple items on the page.
    $("#P2_SEARCH").result(function(event, data, formatted) {
        if (data){
            $("#P2_FIRST_NAME").val(data[1]);
            $("#P2_LAST_NAME").val(data[2]);
            $("#P2_DEPARTMENT").val(data[3]);
            $("#P2_EMAIL").val(data[4]);
        }
    });
});

</script>

And here’s the application process that drives the search results. Note the 2 alter session statements to make the search case-insensitive:

-- This Application Process is named EMPLOYEES_EXTENDED
declare
	l_search varchar2(255);
begin
    execute immediate 'alter session set NLS_SORT=BINARY_CI';
    execute immediate 'alter session set NLS_COMP=LINGUISTIC';

    l_search := replace(wwv_flow.g_x01,'*','%');

    for c1 in (select first_name||' '||last_name name,last_name,first_name,department_name,email
                 from employees_rollup
                where first_name like '%'||l_search||'%'
                   or last_name like '%'||l_search||'%')
    loop
        htp.p(c1.name||'|'||c1.first_name||'|'||c1.last_name||'|'||c1.department_name||'|'||c1.email);
    end loop;
end;

Update: If you want the loading animation, you need to upload the image and add the following in the head of your page template. Thanks Dean Attewell for commenting on this omission.

<style type="text/css">
.ac_loading {
	background: white url('#WORKSPACE_IMAGES#indicator.gif') right center no-repeat;
}
</style>

You can download the updated version 1.2 JavaScript, CSS, and image files here.

Posted in JavaScript / AJAX | 33 Comments »

jApex – A jQuery Plugin for APEX

Posted by Tyler Muth on August 19, 2009

In a previous post I proclaimed that jQuery Selectors Will Change Your Life.  While working on an AJAX centric APEX project, I wanted to use jQuery selectors to post data from an APEX page to an application process so I wrote a jQuery plugin. Over the course of this project the plugin has grown into something that I think the community would find useful.  Essentially it serves the same purpose as Carl Backstrom’s htmldb_get() function, but adds the power of jQuery selectors.  Carl was actually the one that introduced me to jQuery, and I’m sure he would have written something similar, only much better and in half the time.

I think the best way to introduce it is with a few quick examples.  I have a full set of working examples on apex.oracle.com here, but lets start with some simple examples to give you an idea of how it works.

APEX Items

The following code sends every item on an APEX page to the application process “SOME_APP_PROCESS”. It will then popup an alert with the results from the application process. Note that we did not have to specify each item individually, the jQuery selector (documented here) grabs them all.

var options = {
 appProcess: 'SOME_APP_PROCESS',
 pageItems: $(':input'),
 success:
   function(data){
     alert(data);
 }
};

$.jApex.ajax(options);

If we want to send all items inside a div with ID “employeeDiv” and send the results to the Firebug console:

var options = {
 appProcess: 'SOME_APP_PROCESS',
 pageItems: $('#employeeDiv :input'),
 success:
   function(data){
     console.log(data);
 }
};

$.jApex.ajax(options);

Want to send the items P1_NAME, P1_EMAIL, P1_PHONE?

var options = {
 appProcess: 'SOME_APP_PROCESS',
 pageItems: $('#P1_NAME, #P1_EMAIL, #P1_PHONE'),
 success:
   function(data){
   console.log(data);
 }
};

$.jApex.ajax(options);

Other Features

  • You can also send data to the x01-x10 parameters.  There are several examples of this in my sample app.
  • I had already written code to support sending arrays to the f01-f50 parameters used by Tabular Forms.  I really don’t like Tabular Forms and rarely, if ever use them.  However, I figured someone would want / request this functionality so I enhanced it a bit to better support Tabular Forms.  Keep in mind this functionality has not been tested much, so use at your own risk.

Tips

  • $ is the alias for jQuery
  • jQuery plugins almost always have 1 parameter that is object.  Each property of the object is analogous to a traditional JavaScript (or for that matter PL/SQL) parameter.
  • Parameters  can be objects, arrays, scalars or even functions.  Most people are used to scalars, so passing a function in as parameter / property can really be confusing at first.

Download

You can download the jQuery plugin here and the example APEX application here.

Update: I hosted the files in a more permanent location.

Posted in APEX, JavaScript / AJAX | Tagged: , , | 28 Comments »

jQuery Selectors Will Change Your Life

Posted by Tyler Muth on December 17, 2008

OK, maybe not your life but it will absolutely change the way you code JavaScript. If you haven’t used jQuery yet, here’s the description from the jQuery site:

jQuery is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. jQuery is designed to change the way that you write JavaScript.

jQuery is technology agnostic, so you can use it in APEX, J2EE, PHP, .Net, Rails, etc. I’ve been using it with APEX for the last few months and there’s a pretty good chance it will actually be included in the 4.0 release of APEX.

Why are selectors so great? Because they completely change the way you work with JavaScript. Traditionally, people add JavaScript such as onClick() events in the code that renders a page. This is easy for hand-coded HTML or other bare-bones technologies such as PHP (when not using a framework). But in the case of frameworks such as APEX, you actually don’t have access to all of the code that rendered the page. Using selectors, you can get very granular access to any element on the page after it is rendered. For example, the following code changes the style of EVERY link on your page:

$(document).ready(function() {
  $("a").css('text-decoration','none');
});

Or maybe you want to add an onClick event to every div that has a class named “clickme”:

$(document).ready(function() {
    $("div[class=clickme]").click(function() {
       alert('Yep, you clicked me');
    });
});

The following code turns any div on the page red when you hover over it, then white when you move your mouse out of it.

$(document).ready(function() {
    $("div").hover(function() {
       $(this).css('background-color','#ff0000');
    },
        function() {
       $(this).css('background-color','#ffffff');
    });
});

Getting a little more APEX specific, here’s a bit of code that prints all values from the “SAL” column out to the Firebug Console (you must have Firebug installed):

// find all TD's with a class of t20data and headers=SAL.  For each of those, run some function.  console.log is Firebug specific.
$("td[class=t20data][headers=SAL]").each(function(){
  console.log($(this).html());
});

Building on the previous example again, lets change the color of the data in a column based on its value:

// find all TD's with a class of t20data and headers=SAL.  For each of those, run some function.  console.log is Firebug specific.
$("td[class=t20data][headers=SAL]").each(function(){
  sal = parseFloat($(this).html());
  if (sal<1000){
     theColor='green';}
  else if (sal >= 1000 && sal < 2500){
     theColor='yellow';
  }
  else{
       theColor='red';}
   $(this).css('color',theColor);
});&#91;/sourcecode&#93;

Lets continue to build on this example by changing the color of every cell in a row based on the value of the SAL column.  Notice that we walk up the DOM to the TR, then down the DOM to every TD in the row.

&#91;sourcecode language='javascript'&#93;
$("td&#91;class=t20data&#93;&#91;headers=SAL&#93;").each(function(){
 sal = parseFloat($(this).html());
 if (sal<1000){
 theColor='green';}
 else if (sal >= 1000 && sal < 2500){
 theColor='yellow';
 }
 else{
 theColor='red';}
 //$(this)  is currently the TD of the sal column.
 // Walk up to its parent TR, then down to all child TDs.  This effectively loops over every TD in a row
 $(this).parent('tr').children('td').each(function(){
 // $(this) is now the curent TD that is a child of the TR
 $(this).css('color',theColor);
 });
});&#91;/sourcecode&#93;

My final example if from a question on the APEX Forum (<a href="http://forums.oracle.com/forums/message.jspa?messageID=3178134" target="_blank">here's the thread</a>) where someone wanted to replace all instances of the APEX Datepicker item with the jQuery UI Datepicker (I posted about this control <a href="https://tylermuth.wordpress.com/2008/07/16/jquery-datepicker-and-apex/" target="_blank">here</a>).  In APEX 3.1 or greater (not 100% which versions) all datepickers are wrapped in '&lt;fieldset class="datepicker"...'.  This code looks for any of those items on page load and replaces them.

$(document).ready(function(){ 
    $('fieldset[class=datepicker]').find('img').css('display','none');

    $('fieldset[class=datepicker]').find('input[type=text]').datepicker({ 
             showOn: "both", 
             buttonImage: "http://ui.jquery.com/repository/demos_templates/images/calendar.gif", 
             buttonImageOnly: true}); 
}); 

Luis Cabrel pointed out that previous versions of APEX did not have the fieldset code and posted his solution in that same thread:

$(document).ready(function(){
    $('img[title=Calendar]').hide();

    $('img[title=Calendar]').parent().parent().
            find('input[type=text]').datepicker({
            closeAtTop: true,
            dateFormat: "dd-M-yy",
            showOn: "both",
            buttonImageOnly: false,
            duration: 'fast',
            firstDay: 1});
            });

Imagine how much time that would save you if you had even a couple of applications with 10-15 datepickers per app.

The Firefox plugin “Firebug” makes it so much easier to test and develop selectors since you can use HTML > Inspect to find the DOM nodes you want to select, then use the console to test them. console.log() is also infinitely easier to use for debugging than alert() since you don’t have to click “OK” for each event.

I simply can’t stress enough how useful jQuery selectors are. I strongly encourage everyone to take some time to explore them. There is a great tutorial called “Getting Started With jQuery” that goes into more detail. If you have other cool jQuery solutions with APEX, I’d love to see them!

Posted in Application Express, JavaScript / AJAX | Tagged: , , | 19 Comments »

Working on Local JavaScript Files

Posted by Tyler Muth on October 22, 2008

JavaScript can be a nightmare to work with.  Typically, the functions you’re defining are in a separate .js file that’s included in the HEAD of your HTML.  For convenience, I find it easiest to work on a local copy of this file instead of change, ftp to server, refresh page, repeat.  This is especially true in an Application Express (APEX) environment where you would have to upload a new version to the APEX environment via the web interface between each change.  The problem is that most browsers (I tested Firefox, IE 7, Chrome, and Safari) block access to local files called by remote pages.  This is obviously a good thing for security purposes, but it makes it REALLY hard to work with JavaScript.

The solution?  Firefox allows you to add exceptions to this rule on a site-by-site basis.

  1. First, shut down all instances of Firefox (don’t skip this step).
  2. Then, edit or create a user.js file in your profile using this guide. It varies by operating system, but on Vista 64-bit my profile was in “C:\Users\tmuth\AppData\Roaming\Mozilla\Firefox\Profiles“.
  3. Based on this article, add the following lines to your user.js file to allow applications on apex.oracle.com to include local JavaScript files :
    user_pref("capability.policy.localfilelinks.checkloaduri.enabled", "allAccess");
    user_pref("capability.policy.localfilelinks.sites", "http://apex.oracle.com");
    user_pref("capability.policy.policynames", "localfilelinks");
  4. Start Firefox.
  5. Add a link to a local JavaScript file in your application.  Note that there were Path Syntax examples in the “Links to local pages do not work” article.

Now you can edit the file on your local machine, then refresh the web application to reflect those changes.

local-JavaScript

Obviously, once everything is working you’ll need to upload the JavaScript file to the application server (or APEX in this case) and change the reference in the HTML HEAD to point to the version running on the server, not your local machine.

Another solution for smaller functions is to use the Firebug plugin for Firefox, but it just doesn’t work well for really big functions.

Posted in APEX, JavaScript / AJAX | 5 Comments »

jQuery Datepicker and APEX

Posted by Tyler Muth on July 16, 2008

APEX includes a great datepicker, but it is a popup window and there are a number of newer, better looking options out there.  One of my favorites is from the jQuery UI project (direct link to datepicker section).  If you haven’t worked with jQuery yet, it’s well worth a look as it’s very well documented and there are a ton of plugins.  As always, I STRONGLY recommend you use the Firebug plugin for Firefox for any JavaScript and CSS work as it will save you many, many hours, particularly in a hosted environment like APEX.

OK, on to the good stuff.  You can try out the examples here, or download the example application here.  There are several JavaScript and one CSS file inlcuded in the page template, as well as some customized CSS classes.  The header section of page 1 is where the following JavaScript is declared:

function disable_kinda(pThis){
	$x_Style(pThis,'color','#ccc');
	pThis.onfocus=function(){this.blur();}
} 

$(document).ready(function(){
	disable_kinda($x('P1_DATE_ONE'));

	$('#P1_DATE_ONE').datepicker({
		showOn: "both",
		buttonImage: "http://ui.jquery.com/repository/demos_templates/images/calendar.gif",
		buttonImageOnly: true});

	$('#P1_DATE_TWO').datepicker({
		showOn: "both",
		buttonImage: "http://ui.jquery.com/repository/demos_templates/images/calendar.gif",
		buttonImageOnly: true});

	$('#P1_DATE_THREE').datepicker({
		dateFormat: "D, M dd, yy",
		showOn: "both",
		buttonImage: "http://ui.jquery.com/repository/demos_templates/images/calendar.gif",
		buttonImageOnly: true});

	$('#P1_DATE_FOUR').datepicker({
		onSelect: function(date) { doSubmit("DATE_CHANGE");},
		showOn: "both",
		buttonImage: "http://ui.jquery.com/repository/demos_templates/images/calendar.gif",
		buttonImageOnly: true});

	$('#P1_DATE_FIVE').datepicker({
		onClose: function(date) { doSubmit("DATE_CHANGE");},
		rangeSelect: true,
		numberOfMonths: 2,
		minDate: new Date(2008, 5 -1, 1),
		maxDate: new Date(2008, 7 -1, 30),
		showOn: "both",
		buttonImage: "http://ui.jquery.com/repository/demos_templates/images/calendar.gif",
		buttonImageOnly: true});
});

Thanks to Carl Backstrom for the “disable_kinda” function, as well as suggesting I look at jQuery in the first place.

MatjazC posted a good link in the comments to this site, which is the development page for this speciific comments.  The key point there is the direct link on that page to the CSS for this control.  I used the flora.css that ships with jQuery, then modified a few of the classes.  It would be better to start with the this one instead.

Posted in APEX, JavaScript / AJAX | Tagged: | 19 Comments »

JavaScript, can we just be friends?

Posted by Tyler Muth on February 24, 2008

JavaScript and I have some “history”.  See, we were introduced about 8 years ago.  Things were on and off forI_Heart_JavaScript a while, and I really only called her when I was desperate.  We weren’t really right for each other.  She was always lying to me and was impossible to understand.  Frankly, I cheated on her every chance I got with PL/SQL or PHP, and even flirted with Flash a few times.  I constantly told her she was the best, the only one for me just so she would do what I want, while secretly talking badly about her behind her back.  Oh, and she never got along my friends.  When Firefox came over everything was fine, but man you should have seen how she behaved around IE.  I lost all touch with Opera and Safari because it just wasn’t worth the hassle.  We both had some growing up to do, so a couple of years ago, I left her completely.  I heard she’s been seeing some guy named Carl, and that he knew her better than she knew herself… apparently he really treated her well.   ajax do you like me

To be totally honest, I’m not really interested in JavaScript, it’s her younger, hotter sister “Ajax”.  I’ve been trying to get to know Ajax better lately, but JavaScript keeps stepping in and reminding me of our past. I now realize that the only way I have a chance with Ajax is to make things right with JavaScript.

So, I sought help from a relationship counseling practice called YUI Theater. If only I would have found them sooner.  They’re such a good resource to help me understand JavaScript better.  Their lead counselor, Douglas Crockford has taught me so much already.  Crockford has a great series of videos including “Introduction to JavaScript”, “Advanced JavaScript”, and “An Inconvenient API: The Theory of the DOM”.  JavaScript, I’m finally starting to understand you…

Posted in JavaScript / AJAX | 14 Comments »