现在的位置: 首页 > 综合 > 正文

《Ajax开发精要》关于ajaxanywhere 教程三

2014年01月18日 ⁄ 综合 ⁄ 共 12193字 ⁄ 字号 评论关闭

11.3.3  AjaxAnywhere的类库及其用法

AjaxAnywhere使用一个名为aa.jsJavascript文件来处理客户端的全部Ajax操作,包括初始化XMLHttpRequest、获取表单内容、发送Ajax请求、执行回调函数等。aa.js也是使AjaxAnywhere之前必须了解的,至少应该知道其经常用到的APIAjax Anywhere的官方网站提供了相应的Javascript Document,方便快速查找和了解这些API

1AjaxAnywhere的初始化

aa.js中定义了一个AjaxAnywhere对象,针对Ajax的各种操作被抽象成AjaxAnywhere对象的方法,通过这些对象方法完成所需的操作。必要的时候,可以重载这些方法,以便满足个性化的需求。在aa.js文件的末端,AjaxAnywhere对象使用默认的构造方法完成对象实例化。

ajaxAnywhere = new AjaxAnywhere();

ajaxAnywhere.bindById();

所以,所有引用aa.js的页面都可以在Javascript代码段中使用AjaxAnywhere对象的实例ajaxAnywhere

AjaxAnywhere初始化的时候,它在默认的构造函数中完成XMLHttpRequest对象的创建,并保存在AjaxAnywhere对象属性req中。AjaxAnywhere对象默认的构造方法如例程11-23所示。

例程11-23  AjaxAnywhere对象的默认构造方法

function AjaxAnywhere() {

    this.id = AjaxAnywhere.defaultInstanceName;//id,用于生成更新区域的编号等用途

    this.formName = null;//页面表单名称

    this.notSupported = false;//是否支持Ajax

    this.delayBeforeContentUpdate = true;//在更新页面内容之前是否延迟

    this.delayInMillis = 100;//延迟时间

    //初始化XMLHttpRequest对象--req

    if (window.XMLHttpRequest) {

        this.req = new XMLHttpRequest();

    } else if (window.ActiveXObject) {

        try {

            this.req = new ActiveXObject("Msxml2.XMLHTTP");

        } catch(e) {

            try {

                this.req = new ActiveXObject("Microsoft.XMLHTTP");

            } catch(e1) {

                this.notSupported = true;

                /* XMLHTTPRequest not supported */

            }

        }

    }

    //确定浏览器是否支持Ajax

    if (this.req == null || typeof this.req == "undefined")

        this.notSupported = true;

}

2AjaxAnywhere处理Ajax请求

AjaxAnywhere提供两个公共方法处理Ajax请求的发送:submitAJAX(additionalPost Data, submitButton)getAJAX(url, zonesToRefresh)。前者用于发送POST类型的Ajax请求,后者则用于发送GET类型的请求,可以直接在Web页面的表单中或者Javascript代码段直接使用ajaxAnywhere.submitAJAX(additionalPostData, submitButton)或者ajaxAny where. getAJAX (url, zonesToRefresh)向服务器发送Ajax请求。

ajaxAnywhere对象的属性formName保存Ajax所指向的表单名称,只要为其指定表单名称(如果未指定,则默认是Web页面中的第一个表单),submitAJAX(additionalPost Data,submitButton)就能够自动获取指定表单的全部表单域及其值,组成parameterName1 =value1 &parameterName2=value2字符串,这个过程由私有(private)方法preparePostData (submitButton)完成;preparePostData(submitButton)方法遍历表单中的全部元素,将下拉列表、文本框、复选框、单选框等的值自动加入字符串中;submitAJAX方法的参数additionalPostData代表除了表单域值外还要发送给服务器的内容,submitButton则是代表发送操作是否由提交按钮触发的。SubmitAJAX()方法的代码如例程11-24所示。

例程11-24  submitAJAX() 方法发送POST类型请求

AjaxAnywhere.prototype.submitAJAX = function(additionalPostData, submitButton) {

    //如果浏览器不支持Ajax

    if (this.notSupported)

        return this.onSubmitAjaxNotSupported(additionalPostData);

    //附加参数为空

    if (additionalPostData == null || typeof additionalPostData == "undefined")

        additionalPostData = "";

    //id绑定

    this.bindById();

    //获取当前表单对象

    var form = this.findForm();

    //获取表单的action,确定表单提交目标的url

    var actionAttrNode = form.attributes.getNamedItem("action");

    var url = actionAttrNode == null?null:actionAttrNode.nodeValue;

    //如果表单action未设置,则url为当前页面

    if ((url == null) || (url == ""))

        url = location.href;

    //确定请求成功后要重载刷新的页面区域

    var zones = this.getZonesToReload(url, submitButton);

    //如果未设置重载刷新区域,则刷新整个页面

    if (zones == null) {

        if (typeof form.submit_old == "undefined")

            form.submit();

        else

            form.submit_old();

        return;

    }

    //放弃上一次未完成的请求

    this.dropPreviousRequest();

    //设置请求参数,发送类型为POST,请求为异步方式

    this.req.open("POST", url, true);

    this.req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

    this.req.setRequestHeader("Accept", "text/xml");

    //确定要发送给服务器的内容

    var postData = this.preparePostData(submitButton);

    //已设置要重载刷新的区域,将区域名称附加在发送内容中

    if (zones != "")

        postData = '&aazones=' + encodeURIComponent(zones) + "&" + postData + "&" + additionalPostData;

    else

        postData += "&" + additionalPostData;

    //发送Ajax请求

    this.sendPreparedRequest(postData);

}

显然,如果使用AjaxAnywhere自定义标签为Web页面划分指定了刷新区域,则submitAJAX()方法也会将其包含在参数中发送到服务器端。

相对的,getAJAX(url,zoneToRefresh)方法则比较简单。它获取Ajax请求的目标URL,将需要发送的参数附着在这个URL后面,然后调用XMLHttpRequest对象的open()send()方法将其发送除去。getAJAX(url,zoneToRefresh)方法的代码如例程11-25所示。

例程11-25  getAJAX()方法发送GET类型请求

AjaxAnywhere.prototype.getAJAX = function(url, zonesToRefresh) {

    //如果浏览器不支持Ajax,则刷新整个页面

    if (this.notSupported)

        return this.onGetAjaxNotSupported(url);

    //id绑定

    this.bindById();

    //如果为设置刷新区域,则刷新整个页面

    if (zonesToRefresh == null || typeof zonesToRefresh == "undefined")

        zonesToRefresh = "";

    var urlDependentZones = this.getZonesToReload(url);

    if (urlDependentZones == null) {

        location.href = url;

        return;

    }

    //已经设置多个刷新区域

    if (urlDependentZones.length != 0)

        zonesToRefresh += "," + urlDependentZones;

    //放弃上一次未完成的额请求

    this.dropPreviousRequest();

    //确定请求附加的参数

    url += (url.indexOf("?") != -1) ? "&" : "?";

 

    url += "aa_rand=" + Math.random();

    // avoid caching

 

    if (zonesToRefresh != null && zonesToRefresh != "")

        url += '&aazones=' + encodeURIComponent(zonesToRefresh);

    //设置请求参数,发送类型为GET,响应结果为XML文档

    this.req.open("GET", url, true);

    this.req.setRequestHeader("Accept", "text/xml");

    //发送Ajax请求

    this.sendPreparedRequest("");

}

与一般的Ajax应用程序一样,AjaxAnywhere更新页面的操作仍然由回调函数完成,这也是Ajax的机制所定义的。应该注意到,submitAJAX()方法和getAJAX()方法的后面都调用了一个sendPreparedRequest()的方法,只是两者传入的参数内容有所不同而已。AjaxAnywhere对象在该方法中设置必要的请求头信息,为XMLHttpRequest对象指定回调函数,然后才将Ajax请求发送出去。setPreparedRequest()方法如例程11-26所示。

例程11-26  setPreparedRequest()方法

AjaxAnywhere.prototype.sendPreparedRequest = function (postData) {

    //确定Ajax请求回调函数

    var callbackKey = this.id + "_callbackFunction";

    if (typeof window[callbackKey] == "undefined")

        window[callbackKey] = new Function("AjaxAnywhere.findInstance (/"" + this.id + "/").callback(); ");

    this.req.onreadystatechange = window[callbackKey];

    //显示请求正在处理的提示信息

    this.showLoadingMessage();

    //设置请求头参数,当前请求为aaxml请求

    this.req.setRequestHeader("aaxmlrequest", "true");

    //发送请求

    this.req.send(postData);

}

从这个方法可以看出,如果未在Web页面中定义相应的回调函数,则AjaxAnywhere将使用默认的回调函数。

AjaxAnywhere统一使用XML文档来组织服务器返回的响应数据。这个文档采用UTF-8的编码方式,例程11-22所配置的AAFilter也采用UTF-8的方式从请求中获取请求数据。AjaxAnywhere支持页面区域内容刷新、URL跳转、脚本、图像等处理功能,这些处理功能的信息都包含在这个XML文档中。当客户端发送的Ajax请求被正常执行后,AjaxAnywhere即将返回的响应数据组织为符合例程11-27所示文档类型定义文件DTD所规定范式的XML文档。

例程11-27  AjaxAnywhere返回的XML文档的文档类型定义DTD

<?xml version="1.0" encoding="UTF-8" ?>

<!ELEMENT zones ( document*, zone*, script*, exception*, redirect*, image* ) >

<!ELEMENT document (#PCDATA)>

<!ELEMENT zone ( #PCDATA ) >

<!ATTLIST zone name NMTOKEN #REQUIRED >

<!ELEMENT script ( #PCDATA ) >

<!ELEMENT exception ( #PCDATA ) >

<!ATTLIST exception type NMTOKEN #REQUIRED >

<!ELEMENT redirect ( #PCDATA ) >

<!ELEMENT image (#PCDATA) >

例程11-28是一个仅包含zonescriptexceptionredirect节点的示范XML文档。这个文档由AjaxAnywhere的业务类自动组织,与Web应用程序的其他实体类、业务类无关。Web应用程序只需要按照传统的通信机制一样,将响应结果输出即可。

例程11-28  AjaxAnywhere返回的包含响应数据的XML文档

<?xml version="1.0" encoding="UTF-8"?>

<zones>

    <zone name="stateSavingScript"/>

    <zone name="countriesList">

        <![CDATA[

            <select size="10" name="country">

                <option value=IN>India</options>

            </select>

        ]]>

    </zone>

    <script>

        <![CDATA[

            var states = document.getElementsByName("jsf_state_64")

            var trees = document.getElementsByName("jsf_tree_64")

            if(states!=null)

                for(var i=0;i<states.length;i++)

                    if(states[i].tagName.toLowerCase()=="input") {

                        states[i].values="null";

                    }

            if(trees!=null)

                for(var i=0;i<trees.length;i++)

                    if(trees[i].tagName.toLowerCase()=="input")

                        trees[i].values="null";

        ]]>

    </script>

    <script/>

    <script/>

    <exception type="org.apache.jasper.JasperException"><![CDATA[

    org.apache.jasper.JasperException:AjaxAnywhere demo exception

        at org.apache.jasper.servlet.JspServletWraper.service (Jsp Servlet Wrapper.java:372)

        ............

    ]]>

    </exception>

    <redirect><![CDATA[reditected.jsp]]></redirect>

</zones>

AjaxAnywhere默认的回调函数使用XMLHttpRequest对象的responseXML()方法来获取服务器返回的这个XML文档,使用DOM解析。如果XML文档包含documentzone元素,则按照之前设定的更新区域来更新页面中指定区域的内容。如果XML文档中包含script元素,则从元素中提取脚本,使用eval()方法执行。如果服务器响应请求的时候发生系统异常或者HTTP请求异常,则XML文档中会包含exception元素,回调函数即可从元素中获取异常信息,交给this.handleException (type, details)方法做异常处理。如果XML文档中包含redirect元素,则回调函数将其转化为“locarion.href=newURL”的方法,将页面跳转到指定的URL。如果XML文档中包含image元素,则AjaxAnywhere即从服务器获取image对象所指定的图像,将其缓存在浏览器。AjaxAnywhere默认的回调函数如例程11-29所示。

例程11-29  AjaxAnywhere对象默认的构造函数

AjaxAnywhere.prototype.callback = function() {

 

    if (this.req.readyState == 4) {

 

        this.onBeforeResponseProcessing();

        this.hideLoadingMessage();

 

        if (this.req.status == 200) {

           if (this.req.getResponseHeader('content-type').toLowerCase(). substring(0, 8) != 'text/xml')

                alert("AjaxAnywhere error : content-type in not text/xml : [" + this.req.getResponseHeader('content-type') + "]");

 

            var docs = this.req.responseXML.getElementsByTagName("documen t");

          var redirects = this.req.responseXML.getElementsByTagNa me ("redirect");

            var zones = this.req.responseXML.getElementsByTagName("zon e");

            var exceptions = this.req.responseXML.getElementsByTagName ("exception");

            var scripts = this.req.responseXML.getElementsByTagName("scr ipt");

            var images = this.req.responseXML.getElementsByTagName("imag e");

 

            if (redirects.length != 0) {

                var newURL = redirects[0].firstChild.data;

                location.href = newURL;

            }

            if (docs.length != 0) {

                var newContent = docs[0].firstChild.data;

 

                //cleanup ressources

                delete this.req;

 

                document.close();

                document.write(newContent);

                document.close();

            }

 

            if (images.length != 0) {

                var preLoad = new Array(images.length);

                for (var i = 0; i < images.length; i++) {

                    var img = images[i].firstChild;

                    if (img != null) {

                        preLoad[i] = new Image();

                        preLoad[i].src = img.data;

                    }

                }

                if (this.delayBeforeContentUpdate) {

                    delay(this.delayInMillis);

                }

            }

 

            if (zones.length != 0) {

                for (var i = 0; i < zones.length; i++) {

                    var zoneNode = zones[i];

                    var name = zoneNode.getAttribute("name");

                    var fc = zoneNode.firstChild;

 

                    var html = (fc == null)?"":fc.data;

 

                    var zoneHolder = document.getElementById("aazone." + name);

                    if (zoneHolder != null && typeof(zoneHolder) != "undefined") {

                        zoneHolder.innerHTML = html;

                    }

 

                }

            }

            if (exceptions.length != 0) {

                var e = exceptions[0];

                var type = e.getAttribute("type");

                var stackTrace = e.firstChild.data;

                this.handleException(type, stackTrace);

            }

 

            if (scripts.length != 0) {

                for (var $$$$i = 0; $$$$i < scripts.length; $$$$i++) {

                    // use $$$$i variable to avoid collision with "i" inside user script

                    var script = scripts[$$$$i].firstChild;

                    if (script != null) {

                        script = script.data;

                        if (script.indexOf("document.write") != -1) {

                            this.handleException("document.write", "This script contains document.write(), which is not compatible with AjaxAnywhere : /n/n" + script);

                        } else {

                            eval(script);

                        }

                    }

                }

 

                var globals = this.getGlobalScriptsDeclarationsList(scr ipt);

                if (globals != null)

                    for (var i in globals) {

                        var objName = globals[i];

                        try {

                            window[objName] = eval(objName);

                        } catch(e) {

                        }

                    }

            }

 

        } else {

            if (this.req.status != 0)

                this.handleHttpErrorCode(this.req.status);

        }

        this.restoreSubstitutedSubmitButtons();

        this.onAfterResponseProcessing();

    }

}

值得注意的是,在例程11-29所示的默认回调函数中,除了解析XML文档外,在回调函数执行业务逻辑之前和之后,还分别调用了两个可供重载的方法:this.onBeforeResp onse Processing() this.onAfterResponseProcessing()。如果希望在执行回调函数更新页面内容之前处理额外的业务,则可以在适当的位置重载this.onBeforeResponseProcessing()方法。如果希望执行完回调函数更新页面内容之后,还希望继续执行其他逻辑操作,则可以在适当的位置重载this.onAfterResponseProcessing()方法。从aa.js的源码中,可以看到目前这两个方法都未执行任何操作,如例程11-30所示。

例程11-30  onBeforeResponseProcessing()onAfterResponseProcessing()方法

/**

* Override this method to implement a custom action

*/

AjaxAnywhere.prototype.onBeforeResponseProcessing = function () {

};

/**

* Override this method to implement a custom action

*/

AjaxAnywhere.prototype.onAfterResponseProcessing = function () {

};

【上篇】
【下篇】

抱歉!评论已关闭.