Thursday, December 18, 2008

Using "Prompt LOV" to replace "Popup Key LOV"

Another post about jQuery in APEX! :) I am really happy to see the interest the APEX Community has for jQuery.

After reading Dan McGhan's blog post concerning jQuery Impromptu, I decided to try this jQuery extension. For my demo, I decided to replace a "Popup Key LOV (Displays description, returns key value)" with a "Prompt LOV".
I was able to reuse code from previous demos to accelerate the development.

Here is a working example of the Prompt LOV & Calendar demo.

How does it works?

1)I assume you check and understand these demos:
A)Popup in Report
B)AJAX request Page Report
C)jQuery Datepicker

Now... we can continue to the next step. :)

2)Replace the existing Popup LOV with a Prompt. The prompt will display a report from another page(in this case, it's the page 42). The report is requested using AJAX.

This is the JavaScript code you need:

$().ready(function() {
//Replace existing POPUP-LOV
$("#P41_DEPTNO_fieldset a")
.attr('href','#')
.click(function(e){
e.preventDefault();

//request DEPT Report
$.post("wwv_flow.show",
{p_flow_id:$('#pFlowId').attr("value"),
p_flow_step_id:"42",
p_instance:$('#pInstance').attr("value"),
p_request:""},
function(data){
var startTag = '<apex:ajax>';
var endTag = '</apex:ajax>';
var start = data.indexOf(startTag);

if (start > 0) {
data = data.substring(start+startTag.length);
var end = data.indexOf(endTag);
data = data.substring(0,end);
}

//Prompt the user with the LOV
$.prompt(data,{callback: function(v,m){
//Put the original pFlowStepId value to submit the page and process it
$('#pFlowStepId').attr("value","41");
}
});

//Workaround to make the report "pagination" and "order by" work
$('#pFlowStepId').attr("value","42");
}
);
});
});


3)The LOV is a Report Region using an SQL Query.

This is the query used to populate the report:

SELECT '<a href="#" onclick="select_lov_entry(this,'''||dname||''','''||deptno||''');">'||dname||'</a>' AS lov_entry
FROM dept


This is the JavaScript code you need for selecting a value:

function select_lov_entry(pNd,x,y) {
passBack(x,y);
highlight_selection(pNd);
}

//Copy an existing function used by the original POP-UP LOV
//Small modifications are required (we are not referencing an opener window)
function passBack(x,y){
if (document.forms["wwv_flow"].p_t09.length > 1){
var l_field = document.forms["wwv_flow"].p_t09[8];
l_field.value = x;
document.forms["wwv_flow"].p_t08[8].value = y;
}
else{
var l_field = document.forms["wwv_flow"].p_t09;
l_field.value = x;
document.forms["wwv_flow"].p_t08.value = y;
}
if(l_field.getAttribute('onchange') || l_field.onchange){
l_field.onchange();
}
if(!(l_field.disabled || l_field.type == 'HIDDEN')){
l_field.focus();
}
}

function highlight_selection(pNd) {
jQuery(pNd).parent().css({'background-color' : '#55AAFF'});
}

I have to rethink about how to passBack values from the Prompt LOV to the "P41_DEPTNO_fieldset" items. This is for another blog post. ;)

I hope you enjoy this demo!

Friday, December 12, 2008

how to build a Google Gadget with data fetched from an APEX Application Process

Last week, I release a Google Gadget to publish the weekly release of Insum's Web 2.0 Demos.
I'll explain how I did it.

Google Gadget... what's that?!
It's an XML file defining every aspect of your gadget(module). The words "gadget" and "module" can both be used to describe a Google Gadget.

The gadget definition contains 3 sections:

1)Module Preferences
The general information of my gadget is defined in this section. Here is the list of attributes for which I set values.
title
title_url
description
author
author_email
author_location
author_affiliation
author_link
author_photo
author_quote
height
width


I put a default value for the height. Users can update this value using the gadget's settings (build using the "users preferences"). It's also possible to auto-adjust the gadget height.

2)Users Preferences
The settings of my gadget are defined in this section. Here's the list of settings available to the users.
itemCount
fHeight
nHeight


The "itemCount" defines how many items to display. This can be useful if my gadget displays many demos and you don't want the gadget to take too much space on your web page.

The "fHeight" is a flag to set the auto-adjust height to "true" or "false".

The "nHeight" defines the height of the gadget if the auto-adjust height is set to "false".

3)Content
The HTML/JavaScript code is written in this section.

Load the required APIs:

<script src="http://www.google.com/jsapi"></script>
<script>
// Load jQuery
google.load("jquery", "1.2.6", {uncompressed:true});

// Load Google Visualization API
google.load('visualization', '1', {packages: ['table']});
</script>

I use Google Visualization API to display the data in a data grid.

Instantiate an "_IG_Prefs" object:
var prefs = new _IG_Prefs(__MODULE_ID__);

We can access the values of the users preferences using the "getInt" and "getString" functions.
prefs.getInt('fHeight');
prefs.getInt('nHeight');
prefs.getString('itemCount');

Declare the setHeight function

function setHeight() {
_IG_AdjustIFrameHeight(prefs.getInt('fHeight') ? -1 : prefs.getInt('nHeight'));
}
This function will be called each time the data is refreshed.


Declare the draw function

function draw() {
var url = "http://apex.oracle.com/pls/otn/f?p=987654321:204:0:APPLICATION_PROCESS=GET_WEEKLY_RELEASE:::P_REQUEST_ITEM_COUNT:"+prefs.getString('itemCount')+"";

_IG_FetchContent(url, function(data) {
data = eval(data);
if ((nb_demo = data.length) > 0) {
var table_data = new google.visualization.DataTable();
table_data.addColumn('string', 'Title');
table_data.addRows(nb_demo);

for (i = 0; i < nb_demo; i++) {
table_data.setCell(i, 0, data[i].url, data[i].title);
}

table = new google.visualization.Table(document.getElementById('table_div'));
table.draw(table_data, {showRowNumber: true});

// Add our selection handler.
google.visualization.events.addListener(table, 'select', function() {
var row = table.getSelection()[0].row;
window.open(table_data.getValue(row, 0));
});

setHeight();
}
},
{refreshInterval: 0});
}

The "_IG_FetchContent" function will call the GET_WEEKLY_RELEASE Application Process. The callback function handle the data returned by the Application Process. I am using JSON to format the data and I use jQuery to parse it. I set the "refreshInterval option" to 0 because I don't want the results to be cached. This is not recommended!!!... I'll put it to 360(6hrs) when I am done modifying my gadget.


To build my first gadget, I was using an iframe to display the page 204 of my application. Because I didn't like the look and feel, I decided to build it using the Google Visualization API and use an APEX Application Process to feed the data.

I hope I'll see other Google Gadget using APEX Application Processes or MOD_PLSQL.

Friday, December 5, 2008

Google Visualization API : Organizational Chart

It's possible display the relationship of your company employees using the Google Visualization: Organizational Chart.

How it works:
I use jQuery AJAX to call an application process called GET_EMPLOYEES_RELATIONSHIP.
The response is in JSON. I use jQuery to handle the JSON object.
You need to store your chart data inside a "google.visualization.DataTable()" object.
I use the same data table to feed the Table Chart and the Org. Chart.

I created 2 examples.

1) Single Tree View


Your can see a working example of the employees relationship Organizational Chart demo (1).

This is how I define a data table cell:

for (i = 0; i < emp_count; i++) {
l_data_table.setCell(i, 0, data[i].emp_ename);
l_data_table.setCell(i, 1, data[i].mgr_ename);
}

2) Multiple Trees View


Your can see a working example of the employees relationship Organizational Chart demo (2).

This is how I define a data table cell:

for (i = 0; i < emp_count; i++) {
l_data_table.setCell(i, 0, data[i].emp_empno, data[i].emp_ename);
l_data_table.setCell(i, 1, data[i].mgr_empno, data[i].mgr_ename);
}


Enjoy! :)

I'm not done yet with the Google Visualization API... there's still plenty of charts to try out.

Google Gadget - "Insum's Web 2.0 Demos - Weekly Release"

I decided to try the Google Gadget API after reading Bradley's post. I end up creating a notification gadget to publish "Insum's Web 2.0 Demos - Weekly Release". It's now possible to stay up-to-date if you add this gadget to your blog, website, and/or iGoogle page.

I'll write an article about the creation of such gadget.

Enjoy!

Friday, November 28, 2008

Google Visualization API

Using jQuery to handle AJAX request and JSON response is a breeze. After completing yesterday's demo, I discovered the Google Visualization API. I decided to display a report and feed the report with data requested using AJAX.

For this demo, I'll display an Intensity Map with Visitors Locations per Country.



You can see a working example of the AJAX display visitors locations w/ Google Visualization API demo.

1) The Intensity Map
Create an HTML Region.
The source is:

<div id="intensitymap_div" class="google-visualization-intensitymap-container" style="width:440px; height:264px;" />


2) JavaScript Code


<script src="http://www.google.com/jsapi"></script>
<script>
// Load jQuery
google.load("jquery", "1.2.6", {uncompressed:false});

// Load Google Visualization API
google.load('visualization', '1', {packages: ['table', 'intensitymap']});
</script>
<script>
function drawIntensityMap() {
$.ajax({
type: "POST",
url: "wwv_flow.show",
data: {p_flow_id:$('#pFlowId').attr("value"),
p_flow_step_id:$('#pFlowStepId').attr("value"),
p_instance:$('#pInstance').attr("value"),
p_request:"APPLICATION_PROCESS=GET_VISITORS_PER_COUNTRY"
},
dataType: "json",
success: function(data, textStatus){
if ((nb_country = data.length) > 0) {
var map_data = new google.visualization.DataTable();
map_data.addColumn('string', 'Country');
map_data.addColumn('number', 'Visitors');
map_data.addRows(nb_country);

for (i = 0; i < nb_country; i++) {
map_data.setCell(i, 0, data[i].country);
map_data.setCell(i, 1, Number(data[i].visitors));
}

var imap = new google.visualization.IntensityMap($x('intensitymap_div'));
imap.draw(map_data, {});
}
}
});
}

$().ready(function() {
drawIntensityMap();
});
</script>



3) APEX Application Process

DECLARE
l_retval VARCHAR2(32767);
l_rows VARCHAR2(32767);
BEGIN
FOR i IN (SELECT cc.code
,cv.visitors
FROM jq_country_visitors cv
,jq_iso_3166_country_codes cc
WHERE cv.country = cc.country)
LOOP
l_rows := l_rows||'{''country'':'''||i.code||''',''visitors'':'''||i.visitors||'''},';
END LOOP;

l_retval := '['||rtrim(l_rows,',')||']';

htp.prn(l_retval);
END;




A more interesting "Web 2.0" demo could display visitors locations in real time using Google Client Location.

Thursday, November 27, 2008

using AJAX to dynamically translate a report

I decided to push the AJAX integration a little bit further...
Using jQuery and Google AJAX Language API, I was able to translate in "real time" a report. For my example, I decided to translate the records of a table containing the different states of the USA. I realize that this data is not the best for translation demonstration because many words are almost the same no matter what is the spoken language. To have better results, translate the data to Arabic, Chinese, Japanese or Korean.



You can try a working example of the Dynamic Translation w/ Google AJAX Language API demo.

1) Report (Region Definition)
Source:
SELECT     st
,initcap(state_name) AS state_name_en
,initcap(state_name) AS state_name
FROM demo_states

N.B. I had to use the initcap function on my data. Google translation engine is case sensitive. (I need to find more documentation to help with this issue.)


2) Report (Column Attributes)
STATE_NAME_EN:
Column Formatting > CSS Class: translate_src

STATE_NAME:
Column Heading : State Name (<span id="src">english</span>)
Column Formatting > CSS Class: translate_dst

3) Language Selection
I created a Select List item (P35_DST) inside my region "Translate". The options of this Select List are added dynamically when the "init_language_selection_tool" function is called.


4) JavaScript code

<script src="http://www.google.com/jsapi"></script>
<script>
// Load jQuery
google.load("jquery", "1.2.6", {uncompressed:false});

// Load Google AJAX Language API
google.load("language", "1");
</script>
<script>
function translate_report(pFrom, pTo) {
if (pTo != '-1' && pTo != "") {
$(".translate_src").each(function(i){
var l_translation = $(this);
google.language.translate(l_translation.text(), pFrom, pTo, function(result) {
if (!result.error) {
l_translation.parent().next().children().text(result.translation);
}
});
});
$s('src',$('#P35_DST option:selected').text());
}
}

//overwrite existing $a_report function
function $a_report(G,D,F,C,A){
lThis=$u_js_temp_drop();
var B=$x("report_"+G+"_catch");
B.id="report_"+G+"_catch_old";
var E="p="+$v("pFlowId")+":"+$v("pFlowStepId")+":"+$v("pInstance")+":FLOW_PPR_OUTPUT_R"+G+"_";

if(!!A){
E+=A+"::RP&fsp_region_id="+G
}
else{
E+="pg_R_"+G+":NO&pg_max_rows="+F+"&pg_min_row="+D+"&pg_rows_fetched="+C
}

var H=new htmldb_Get(null,null,null,null,null,"f",E);
var I=H.get();
lThis.innerHTML=I;
B.innerHTML=$x("report_"+G+"_catch").innerHTML;
B.id="report_"+G+"_catch";
lThis.innerHTML="";

translate_report("en",$("#P35_DST").val());

return
}

function init_language_selection_tool() {
var dst = $x('P35_DST');
var i=0;
for (l in google.language.Languages) {
var lng = l.toLowerCase();
var lngCode = google.language.Languages[l];
if (google.language.isTranslatable(lngCode)) {
dst.options.add(new Option(lng, lngCode));
}
}
google.language.getBranding('branding');
}

function init_language_selection_event() {
$("#P35_DST").change(function(){
translate_report("en",$(this).val());
});
}

$().ready(function() {
init_language_selection_tool();
init_language_selection_event();
});
</script>


Web 2.0 offers lots of possibilities. Gotta try them all! ;)
I speak French and English. I would like to have feedback about the translation of the other languages.

Tuesday, November 25, 2008

Google AJAX Libraries API

I previously posted an article called "How to... Load the jQuery JavaScript Library". I don't think a gave enough information about why I decided to use the Google AJAX Libraries API. I'll explain a little more this time.

I decided to try the Google AJAX Libraries API because I personally like Google services. :)
I decided to keep using this library because I don't need to worry about the hosting of my .js files. It's also possible to call a compressed or uncompressed version of jQuery and jQuery UI. It's also possible to call older version of a library.
I didn't have bad/slow speed issue using the Google AJAX Libraries API.
I'm using this technique for my web development. If I have an application working on an Intranet, I will host the .js file on my web server.

links:
http://code.google.com/apis/ajaxlibs/
http://code.google.com/apis/ajaxlibs/documentation/

multiple regions under a single tab

Last week, I write about using jQuery UI Tabs to show each region of my page under a tab. Denes Kubicek send me a message asking if it was possible to display more than one region under a single tab. The answer is yes. But how? I tried 3 different ways to do it. I'll first explain the better/simpliest solution.

First, I made a page copy of my last demo (page 30). Don't forget to add the static ID for the regions on the copied page (page copy removes regions static ID)... this is probably a bug in APEX.

1) First solution (working)

A.
I modified the source of the first region called "TABS".





The Region Source is:

<div id="my_tabs">
<ul>
<li><a href="#form_report"><span>My Form & Report</span></a></li>
<li><a href="#my_calendar"><span>My Calendar</span></a></li>
</ul>
</div>
B.
Add the jQuery code to my page HTML header.


<script src="http://www.google.com/jsapi"></script>
<script>
// Load jQuery
google.load("jquery", "1.2.6", {uncompressed:false});
google.load("jqueryui", "1.5.2", {uncompressed:false});
</script>
<link rel="stylesheet" href="&WORKSPACE_IMAGES.my_tabs_01.css" type="text/css" media="screen" title="Flora (Default)">
<script src="&WORKSPACE_IMAGES.jquery.cookie.js"></script>
<script>
$().ready(function() {
//create a DIV element around 2 regions ("my form" and "my report")
$("#my_form, #my_report").wrapAll('<div id="form_report"></div>');

//create the Tabs
$("#my_tabs > ul").tabs(
{cookie: {expires: 1}
}
);

//set the Tabs width
$("ul.ui-tabs-nav").css({width:"350px"});
});
</script>

The solution was to use jQuery wrapAll function to put a DIV aroung 2 regions. In this example, my 2 regions were following each other (see regions sequence). If your regions are not following each other, read the jQuery Manipulation documentation to find how to move those regions inside a DIV. The DIV can be created using jQuery (or see Part2 for an alternate solution).

You can try a working example of my Multiple Regions inside a single Tab demo.


2) Second solution (working)

This solution is less elegant because it requires the developer to add 2 HTML Regions on the page.
The first region goes before the region "My Form".
The source code is the opening tag of the DIV: <div id="form_report">
The second region goes after the region "My Report".
The source code is the closing tag of the DIV: </div>

I didn't like the idea of creating HTML Regions for this purpose so I decided not to continue with this solution.

3) Third solution (not working)

I change the display point of my regions "My Form" and "My Report" from "Page Template Body" to "REGION_POSITION_04". I had to find a way to select this "display point" so it can be the content of a tab. I had display problems so I decided not to continue with this solution.



This should helps creating more advanced UI!
Have fun :)

Thursday, November 20, 2008

A Great Success

The official release of Insum's jQuery Demo Application was a week ago.
What can I say? Thanks to the APEX community for making this project a real success!
Using Google Analytics, I was able to track your every move MUHAHAA! :) We received over 300 unique visitors during this period.

Thanks again.

Now, back to work!

Wednesday, November 19, 2008

Tabs to choose which region to display

It's possible to reduce the space required by regions on a page. How? Use tabs to dynamically choose which region to display. jQuery UI Tabs makes it easy for us to do so.

You can try a working example of my jQuery UI Tabs in APEX demo.

1.
For this example, I need 4 regions.

The first region called "TABS" is an HTML Region with template "No Template".




The Region Source is:

<ul>
<li><a href="#my_form"><span>My Form</span></a></li>
<li><a href="#my_report"><span>My Report</span></a></li>
<li><a href="#my_calendar"><span>My Calendar</span></a></li>
</ul>


This list holds 3 tabs. One tab for each of the region I want to display. Notice the "ahref" values. Each value correspond to a region static ID.

2.
Now, I create the 3 regions I want to display using the tabs.
First, create "My Form" Region and set his static ID to "my_form".
Second, create "My Report" Region and set his static ID to "my_report".
Third, create "My Calendar" Region and set his static ID to "my_calendar".

3.
Add the jQuery code to my page HTML header.


<script src="http://www.google.com/jsapi"></script>
<script>
// Load jQuery
google.load("jquery", "1.2.6");
</script>
<link rel="stylesheet" href="&WORKSPACE_IMAGES.my_tabs_01.css" type="text/css" media="screen" title="Flora (Default)">
<script src="&WORKSPACE_IMAGES.jquery.ui.all.js"></script>
<script src="&WORKSPACE_IMAGES.jquery.cookie.js"></script>
<script>
$().ready(function() {
$(document).ready(function(){
//set ID value for column containing "My Form", "My Report" and "My Calendar"
$("#my_form").parent().attr("id","my_tabs");

//create the Tabs
$("#my_tabs > ul").tabs(
{cookie: {expires: 1}
}
);

//set the Tabs width
$("ul.ui-tabs-nav").css({width:"420px"});
});
});
</script>


N.B.
1) I had to set manually the Tabs width because displaying "My Report" had the effect of displaying the tabs on 2 lines.
2) I need to use the jQuery Cookie Plugin to remember the last selected tabs. This is useful when displaying "My Calendar" and switching mode (or any action causing the page to submit).
3) A CSS file is required to display the tabs. I modified the CSS file used by the official demo for jQuery UI Tabs.


I'll be working on a similar demo. The tabs content will be requested using AJAX.

Monday, November 17, 2008

AJAX get Page Report w/ loading animation

When using AJAX to request new page content, loading animations are useful because the users won't see their browser's status bar loading animation.

You can try a working example of my AJAX get Page Report w/ loading animation demo.

1.
Add on your page a "Report Region" using an SQL Query for the data source.
Identification > Static ID: p27_report_drop
Source > Region Source: select null from dual where 1 = 2
Header and Footer > Region Header: <img src="http://www.blogger.com/i/processing3.gif" />

You should see an empty report with a loading animation.


2.
Add the jQuery code to request page 27 Report Region.

<script src="http://www.google.com/jsapi"></script>
<script>
// Load jQuery
google.load("jquery", "1.2.6");
</script>
<script>
$(function() {
// send request
$.post("wwv_flow.show",
{p_flow_id:$('#pFlowId').attr("value"),
p_flow_step_id:"27",
p_instance:$('#pInstance').attr("value"),
p_request:""},
function(data){
var startTag = '<apex:ajax>';
var endTag = '</apex:ajax>';
var start = data.indexOf(startTag);

if (start > 0) {
data = data.substring(start+startTag.length);
var end = data.indexOf(endTag);
data = data.substring(0,end);
}

$("#p27_report_drop td.t4RegionBody").html(data);

//Workaround to make the report "pagination" and "order by" work
$('#pFlowStepId').attr("value","27");
}
);
});
</script>


The new content will overwrite the loading animation.


The feedback offered to the users can make the difference between two similar applications. Make sure you give enough feedback but don't exaggerate. You don't want to quickly display loading animations and then overwrite them in a fraction of time. The users will see flickers and they might get confuse about what's going on. ;)

Friday, November 14, 2008

dynamic rating system

Social applications are more and more popular. Users want to feel they are not alone using the application. A good way to achieve this is to give them the possibility to rate an article. Rating something shouldn't be a long process. Users don't expect to have a complete page reload for such a small action. The solution is to use AJAX to send the user's rating and to retrieve the average rating. jQuery will be used to handle events, send AJAX requests, and display rating choices and results.

You can try a working example of my dynamic rating system.

1.
Add the rating choices (I decided to use the stars images found in the /i/ of APEX). You'll probably want to add this "controller" at the top or the bottom of the content you want the users to rate.

<div id="rating">
Please rate: <img id="rating_choices" src="/i/stars5.gif" usemap="#map_rating_choices" />
</div>
<map id="map_rating_choices" name="map_rating_choices">
<area coords="6,0,15.8,12" value="1"/>
<area coords="15.9,0,25.7,12" value="2"/>
<area coords="25.8,0,35.6,12" value="3"/>
<area coords="35.7,0,45.5,12" value="4"/>
<area coords="45.6,0,58,12" value="5"/>
</map>


2.
Add the jQuery code to handle the mouse over and the mouse click events.

<script src="http://www.google.com/jsapi"></script>
<script>
// Load jQuery
google.load("jquery", "1.2.6");
</script>
<script>
$(function() {
//apply mouseover handlers
$("#map_rating_choices area").mouseover(function(e){
e.preventDefault();
$("#rating_choices").attr("src","/i/stars"+$(this).attr("value")+".gif");
});

//apply click handlers
$("#map_rating_choices area").click(function(e){
// stop normal link click
e.preventDefault();

// send request
$.ajax({
type: "POST",
url: "wwv_flow.show",
data: {p_arg_names:"P2_RATING",
p_arg_values:$(this).attr("value"),
p_flow_id:$('#pFlowId').attr("value"),
p_flow_step_id:$('#pFlowStepId').attr("value"),
p_instance:$('#pInstance').attr("value"),
p_request:"APPLICATION_PROCESS=NEW_RATING"
},
dataType: "json",
success: function(data, textStatus){
$("#rating").html(
"Thanks for rating, current average: " +
'<img src="/i/stars'+data[0].average+'.gif" alt="'+data[0].average+'" />'+
", number of votes: " +
data[0].count
);
}
});
});
});
</script>


3.
Create an application process to insert the new rating in the database. The same process will return the results using JSON.

DECLARE
l_rating NUMBER := to_number(:P2_RATING);
l_retval VARCHAR2(32767);
l_average NUMBER;
l_count NUMBER;
BEGIN
INSERT INTO JQ_RATINGS(rating) VALUES(l_rating);
COMMIT;

SELECT round(avg(rating))
,count(*)
INTO l_average
,l_count
FROM jq_ratings;

l_retval := '[{''average'':'''||l_average||''',''count'':'''||l_count||'''}]';

htp.prn(l_retval);
EXCEPTION
WHEN others THEN
htp.prn('');
END;



Have fun building your own dynamic rating system!

Wednesday, November 12, 2008

How To... Load the jQuery JavaScript Library

Like any JavaScript API, jQuery needs to be loaded on your web page. To do so, you can host the .js file on your server or call it using Google AJAX API Loader. Using Google's AJAX Libraries API is my favorite choice when I am dealing with an online application. This might not be possible for an intranet application.


This is the HTML code to load jQuery using Google's AJAX API Loader.
<script src="http://www.google.com/jsapi"></script>
<script>
// Load jQuery
google.load("jquery", "1.2.6");
</script>

Integrating jQuery

With my colleagues at Insum Solutions, we started a demo application of what can be done with jQuery a JavaScript Library. So far, I have been impressed by the simplicity of the tool.

Our demo application of jQuery and APEX is available right now! Give it a try, the url is: http://www.insum.ca/jquery.

We are waiting for your comments and suggestions.
You can contact us at demo@insum.ca.

Beginning of a Great Adventure

Hello APEX enthusiasts!

This is the first article of many. I will use this blog to help the APEX community achieve more. I will write basic and advanced guides/tutorials that should be interesting for both APEX beginners and pros.

I hope we will have a great journey together! ;)