Home > .NET, Ajax, Aqualogic, Javascript > Ajax on Aqualogic portals

Ajax on Aqualogic portals

2013/02/04

I’m no more (for now…) working on Aqualogic Portals: i began years ago , when the original publisher was Plumtree, then the technology was buyed from BEA and Oracle subsequently acquired BEA; i don’t know of new releases.

I wrote many “portlets” (originally termed “gadgets”) so i have fighted with the well knowed (from Aqualogic developers) javascript problem : that is your javascript routines in the Aqualogic environment (in the portlet code, in practice) are “gatewayed”.

The “gatewaying” was a strange feature: you wrote your javascript functions and behind the curtains the functions names at runtime were changed adding a special marker $$PORTLET_ID$$ if not present in the function name: an internal identifier for the current portlet.

Another trick to know: if you was using the same id for an html object in more than one portlet it was necessary to add the special marker so the portlet javascript code can refer the correct object , for example

<span id="spaWorkingImage$$PORTLET_ID$$" style="display: none">

At runtime examining the page source the marker was substituted with numbers; this caused a wide array of problems, first of all there was no thirdy party Ajax library (Microsoft Ajax toolkit for example) usable : the first call was working , the subsequent calls no because the gatewaying was changing under the covers the routines names.

Obviously was promised to a customer the use of “not page refreshing” features… so i struggled in order to find a solution.

I went to a minimal but working solution , that could be still useful to someone.

My approach was to write minimal ajax routines using the $$PORTLET_ID$$ , and this is the latest code written from me five years ago:

<script type="text/javascript" language="javascript"> 
var objAquaXHttp; 
 
function getXmlHttpObject$$PORTLET_ID$$(){ 
    try{
        objAquaXHttp = null;
        if (typeof XMLHttpRequest != "undefined"){
            objAquaXHttp = new XMLHttpRequest();
            return objAquaXHttp;  
        }
        else if (window.ActiveXObject){              
            var aVersions = ["MSXML2.XMLHttp.5.0","MSXML2.XMLHttp.4.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp","Microsoft.XMLHttp"];
            for (var i = 0; i < aVersions.length; i++) {
                try {
                    objAquaXHttp = new ActiveXObject(aVersions[i]);
                    return objAquaXHttp;
                } catch (ex2) {
                    //Do nothing
                }
            }
            throw new Error("MSXML not installed.");        
        }                      
    }
    catch(ex){
        var strErr = "\nError getXmlHttpObject$$PORTLET_ID$$ num.: " + (ex.number & 0xFFFF);
        strErr += "\nDescription: " + ex.description;
        window.alert(ex + strErr);
    }                                                   
} 
 
function stdAjaxCall$$PORTLET_ID$$(urlWithQueryString, callbackFunction){ 
    try{
        objAquaXHttp = getXmlHttpObject$$PORTLET_ID$$();
        if (objAquaXHttp == null){
            window.alert ("Browser does not support HTTP Request.");
            return;
        } 
        objAquaXHttp.onreadystatechange = callbackFunction;
        objAquaXHttp.open("GET", urlWithQueryString, true);
        objAquaXHttp.send(null);                   
    }
    catch(ex){
        var strErr = "\nError stdAjaxCall$$PORTLET_ID$$ num.: " + (ex.number & 0xFFFF);
        strErr += "\nDescription: " + ex.description;
        window.alert(ex + strErr);
    }                                                   
}         
 
function stdAjaxCallPOST$$PORTLET_ID$$(urlWithoutQueryString, xmlString ,callbackFunction){ 
    try{
        objAquaXHttp = getXmlHttpObject$$PORTLET_ID$$();
        if (objAquaXHttp == null){
            window.alert ("Browser does not support HTTP Request.");
            return;
        } 
        objAquaXHttp.onreadystatechange = callbackFunction;
        objAquaXHttp.open("POST", urlWithoutQueryString, true);
        objAquaXHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded") ;
        objAquaXHttp.setRequestHeader("Content-length", xmlString.length) ;
        objAquaXHttp.setRequestHeader("Connection", "close") ;
        objAquaXHttp.send(xmlString);                   
    }
    catch(ex){
        var strErr = "\nError stdAjaxCallPOST$$PORTLET_ID$$ num.: " + (ex.number & 0xFFFF);
        strErr += "\nDescription: " + ex.description;
        window.alert(ex + strErr);
    }                                                   
}             
 
function getXMLFromResponse$$PORTLET_ID$$(objHttp){
    var y;
    var x = objHttp.responseXML.childNodes;
    for (i = 0 ;i < x.length; i++){ 
        if (x[i].nodeType == 1){ 
            y = x[i].childNodes;
            for (j = 0; j < y.length; j++){ 
                if (y[j].nodeType == 1){ 
                    if (y[j].childNodes[0] != null)
                        return y[j].childNodes[0].nodeValue;
                    else
                        return '';
                } 
            }
        }
    }       
}        
</script>

Is very plain Javascript because i tried to use the module pattern, but there were strange problems.

This code define a public variable objAquaXHttp , and was calling aspx pages with an XML response type , used as pseudo web services.

In order to create a call we need the address, for example

var strUrl = CurrentAppPath$$PORTLET_ID$$ + "GetCitiesByProv.aspx" + "?q=" + encodeURIComponent(window.document.getElementById('listProvince').value);

In this case we obtain the path of the GetCitiesByProv.aspx and assigned to the variable CurrentAppPath$$PORTLET_ID$$, and then we create the complete url for a GET call.

But the real solution, the silver bullet, is the call (in the example from a onBlur event on a textbox):

stdAjaxCall$$PORTLET_ID$$(PTPortlet.getPortletByID($$PORTLET_ID$$).transformURL(strUrl), txtCityOnBlurCallback$$PORTLET_ID$$);

Note the transformURL: it does the magic of change the url as the Aqualogic portal does; and as in any async Ajax call we assign a callback (txtCityOnBlurCallback$$PORTLET_ID$$)

The pseudo web services is simple, the aspx is only a header:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="GetCitiesByProv.aspx.cs" Inherits="ShipperPickupAddressDetails.GetCitiesByProv" %>

And the code behind , in my sample (is ASP.NET 2.0… no Task, async, await…):

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Text;
 
namespace ShipperPickupAddressDetails
{
    public class GetCitiesByProv : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            Response.Clear();
            Response.ContentType = "text/xml";
            Response.ContentEncoding = Encoding.UTF8;
            System.Xml.XmlTextWriter objX = new System.Xml.XmlTextWriter(Response.OutputStream, Encoding.UTF8);
            objX.WriteStartDocument();
            //
            objX.WriteStartElement("root");
            //   Request.Form.ToString() if POST call
            theDataLayer objData = new theDataLayer();
            string strProvince = Request.QueryString["q"].ToString();
            const string strChar39 = "'";
            const string strApo = "'";
            try
            {
                DataSet ds = objData.ReadCity(strProvince);  
                DataTable dt = new DataTable();
                DataColumn dc = new DataColumn("City");
                dt.Columns.Add(dc);
                DataRow dr = null;
                StringBuilder objString = new StringBuilder();
                // add cities of province in a datatable
                if (ds != null && ds.Tables[0].Rows.Count > 0)
                {
                    dr = dt.NewRow();
                    dr[0] = "Select...  ";
                    dt.Rows.Add(dr);
                    for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
                    {
                        string city = ds.Tables[0].Rows[i]["City"].ToString();
                        if (!city.Equals(String.Empty))
                        {
                            dr = dt.NewRow();
                            dr[0] = city;
                            dt.Rows.Add(dr);
                        }
                    }
                }
                string strItem = String.Empty;
                objString.Append("<select id='listCitySugg'  style='width: 250px;' onchange='javascript:listCitySugg_onchangeAddressManager(this);'>");
                foreach (DataRow objRow in dt.Rows)
                {
                    strItem = objRow[0].ToString().Replace(strApo, strChar39);
                    objString.Append("<option value='" + strItem + "'>" + objRow[0].ToString());
                }
                objString.Append("</select>");
                objX.WriteElementString("data", objString.ToString());
                //
                objX.WriteEndElement();
                objX.WriteEndDocument();
                objX.Flush();
            }
            catch (Exception)
            {
                throw ;
            }
            finally()
            {
                if(objX  != null)
                   objX.Close();
                Response.End();     
            }
        }
    }
}


The code outputs an XML string, which is managed from the callback:

function txtCityOnBlurCallback$$PORTLET_ID$$(){ 
    try{  
        if (objAquaXHttp.readyState == 4 || objAquaXHttp.readyState == "complete"){ 
            if (objAquaXHttp.status == 200) {
                var strResult = getXMLFromResponse$$PORTLET_ID$$(objAquaXHttp);             
                window.document.getElementById('spaListCitySugg').innerHTML = strResult ;
            }else{
                window.alert('Error in txtCityOnBlurCallback, status = ' +  objAquaXHttp.status);
            }
        } 
    }
    catch(ex){
        var strErr = "\nError txtCityOnBlurCallback num.: " + (ex.number & 0xFFFF);
        strErr += "\nDescription: " + ex.description;
        window.alert(ex + strErr);
    }                   
}  

So we can dinamically inject html code into a div and generate on the fly without a postback cities items of a province into an html select, in our Aqualogic portlet.

Advertisements
Categories: .NET, Ajax, Aqualogic, Javascript
%d bloggers like this: