PDA

View Full Version : Ext (datastore and treeloader) and ASP.Net Webservices



DragonFist
21 Sep 2007, 2:43 PM
I have been working on a project that uses asp.net webservices and had a fair amount of trouble to get them working with grids and trees. At first I tried using MS AJAX Library to handle my needs and edited another's extention of dataproxy to get it to work. Unfortunately, it seems MS only allows one async call at a time. If another starts before the first comes back, it just kills the firsts one.

So, in looking for a solution I found the AjaxEngine http://www.mathertel.de/AJAX/default.aspx

I got it working with some changes and by extending the dataproxy and the treeloader. I figured I'd share these.

I have only been programming in Javascript for about a month and have only been programming in general for less than a year. So, I am sure these could be greatly improved. I'd consider them hacks to some degree as I had to change the ajaxengine core file in order for them to work. However, I have seen that others have had need to work with ASP.net webservices and perhaps this can be of use.

Please feel free to improve on these. I only ask that you post your changes here so all can benefit.

Here is a link to the work started by Brendan Carroll for handling the MS AJAX Library if you need that:
http://extjs.com/forum/showthread.php?t=8587&highlight=asp+webservice+ajax


Here is the extention of the dataproxy to connect to a webservice via ajaxengine


AjaxEngineProxy = function(conn){
AjaxEngineProxy.superclass.constructor.call(this);
Ext.apply(this,conn);
};

Ext.extend(AjaxEngineProxy, Ext.data.DataProxy, {
load : function(params, reader, callback, scope, arg){
var AjaxEngineProxy = this.extraParams;
var userContext={callback:callback,reader:reader,arg:arg,scope:scope};


//AjaxEngineProxy = params;
AjaxEngineProxy.userContext = userContext;
switch (this.isAsync){
case true:
AjaxEngineProxy.func = Function.createDelegate(this, this.loadResponse);
AjaxEngineProxy.onException = Function.createDelegate(this, this.loadResponse);

AjaxEngineProxy(params);
break;
case false:
AjaxEngineProxy.syncFunc = Function.createDelegate(this, this.loadResponse);
var r = AjaxEngineProxy(params, userContext);
//loadResponse(r,userContext);
break;
default:
AjaxEngineProxy.func = Function.createDelegate(this, this.loadResponse);
AjaxEngineProxy.onException = Function.createDelegate(this, this.loadResponse);
AjaxEngineProxy(params, userContext);
break;
}



},

loadResponse : function(response,userContext){
var a = response;
var b = userContext;
if(response._message){
this.fireEvent("loadexception", response._message);
userContext.callback.call(userContext.scope, null,userContext.arg, false);
return;
}

var result;
try {
//alert(response);
var tempresponse = response;
response = Ext.util.JSON.decode(response);
response.responseText = tempresponse;
result = userContext.reader.read(response);


}catch(e){
this.fireEvent("loadexception",e);
userContext.callback.call(userContext.scope, null,userContext.arg, false);
return;
}

//call loadRecords
userContext.callback.call(userContext.scope, result, userContext.arg, true);
}

});


// var ds = new Ext.data.Store({
// //proxy: new Ext.data.HttpProxy({method:'POST', url:'Service.asmx/HelloWorld'}),
// proxy: new AjaxEngineProxy({ params : {'ScheduleDate' : shiftDate,'CompanyName' : CompanyName}, extraParams: proxies.Service.Method, isAsync: true}),
// reader: new Ext.data.XmlReader({record: 'Item',id: 'ASIN'}, ['Author', 'Title', 'Manufacturer', 'ProductGroup'])
// });



And here is the extention of the treeloader that will take data from a asp.net webservice via ajaxengine.



AjaxEngineTreeLoader = function(config){
this.baseParams = {};
this.requestMethod = "POST";
Ext.apply(this, config);

this.addEvents({

"beforeload" : true,

"load" : true,

"loadexception" : true
});
AjaxEngineTreeLoader.superclass.constructor.call(this);

};

Ext.extend(AjaxEngineTreeLoader, Ext.tree.TreeLoader, {

uiProviders : {},


clearOnLoad : true,

load : function(node, callback){
if(this.clearOnLoad){
while(node.firstChild){
node.removeChild(node.firstChild);
}
}
if(node.attributes.children){ // preloaded json children
var cs = node.attributes.children;
for(var i = 0, len = cs.length; i < len; i++){
node.appendChild(this.createNode(cs[i]));
}
if(typeof callback == "function"){
callback();
}
}
else if(this.AjaxEngineProxy){
this.CallAjaxEngineProxy(node, callback);
}
else if(this.dataUrl){
this.requestData(node, callback);
}
},

getParams: function(node){
var buf = this.baseParams;

for (var attrib in node.attributes){
if(attrib != 'text' && attrib != 'draggable'){
buf[attrib] = node.attributes[attrib] ;
}
}

buf.node = node.id;

return buf;
},

requestData : function(node, callback){
if(this.fireEvent("beforeload", this, node, callback) !== false){
this.transId = Ext.Ajax.request({
method:this.requestMethod,
url: this.dataUrl||this.url,
success: this.handleResponse,
failure: this.handleFailure,
scope: this,
argument: {callback: callback, node: node},
params: this.getParams(node)
});
}else{
// if the load is cancelled, make sure we notify
// the node that we are done
if(typeof callback == "function"){
callback();
}
}
}, CallAjaxEngineProxy : function(node, callback){
if(this.fireEvent("beforeload", this, node, callback) !== false){
this.transId = true;
var currentAjaxEngineProxy = this.AjaxEngineProxy;
currentAjaxEngineProxy.func = this.handleResponse.createDelegate(this);
currentAjaxEngineProxy.onException = this.handleFailure.createDelegate(this);
currentAjaxEngineProxy.argument = {callback: callback, node: node};
currentAjaxEngineProxy(this.getParams(node));

}else{
// if the load is cancelled, make sure we notify
// the node that we are done
if(typeof callback == "function"){
callback();
}
}
},

isLoading : function(){
return this.transId ? true : false;
},

abort : function(){
if(this.isLoading()){
Ext.Ajax.abort(this.transId);
}
},


createNode : function(attr){

if(this.baseAttrs){
Ext.applyIf(attr, this.baseAttrs);
}
if(this.applyLoader !== false){
attr.loader = this;
}
if(typeof attr.uiProvider == 'string'){
attr.uiProvider = this.uiProviders[attr.uiProvider] || eval(attr.uiProvider);
}
return(attr.leaf ?
new Ext.tree.TreeNode(attr) :
new Ext.tree.AsyncTreeNode(attr));
},

processResponse : function(response, node, callback){
var json = response.responseText;
//alert(json);
try {
var o = eval("("+json+")");
for(var i = 0, len = o.length; i < len; i++){
var n = this.createNode(o[i]);
if(n){
node.appendChild(n);
}
}
if(typeof callback == "function"){
callback(this, node);
}
}catch(e){
this.handleFailure(response);
}
},

handleResponse : function(response){
this.transId = false;
var a = response.argument;
this.processResponse(response, a.node, a.callback);
this.fireEvent("load", this, a.node, response);
},

handleFailure : function(response){
this.transId = false;
var a = response.argument;
this.fireEvent("loadexception", this, a.node, response);
if(typeof a.callback == "function"){
a.callback(this, a.node);
}
}
});


and here is my version of the ajax.js from the ajaxengine/ajaxcore folder. There are some changes to it that allow the passing of params via the above two proxies. I'm sure the above proxies can be rewritten so this doesn't need changing but that's not the case now.



// ajax.js
// Common Javascript methods and global objects
// Ajax framework for Internet Explorer (6.0, ...) and Firefox (1.0, ...)
// Copyright (c) by Matthias Hertel, http://www.mathertel.de
// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
// More information on: http://ajaxaspects.blogspot.com/ and http://ajaxaspekte.blogspot.com/
// -----
// 05.06.2005 created by Matthias Hertel.
// 19.06.2005 minor corrections to webservices.
// 25.06.2005 ajax action queue and timing.
// 02.07.2005 queue up actions fixed.
// 10.07.2005 ajax.timeout
// 10.07.2005 a option object that is passed from ajax.Start() to prepare() is also queued.
// 10.07.2005 a option object that is passed from ajax.Start() to prepare(), finish()
// and onException() is also queued.
// 12.07.2005 correct xml encoding when CallSoap()
// 20.07.2005 more datatypes and XML Documents
// 20.07.2005 more datatypes and XML Documents fixed
// 06.08.2005 caching implemented.
// 07.08.2005 bugs fixed, when queuing without a delay time.
// 04.09.2005 bugs fixed, when entering non-multiple actions.
// 07.09.2005 proxies.IsActive added
// 27.09.2005 fixed error in handling bool as a datatype
// 13.12.2005 WebServices with arrays on strings, ints, floats and booleans - still undocumented
// 27.12.2005 fixed: empty string return values enabled.
// 27.12.2005 enable the late binding of proxy methods.
// 21.01.2006 void return bug fixed.
// 18.02.2006 typo: Finsh -> Finish.
// 25.02.2006 better xmlhttp request object retrieval, see http://blogs.msdn.com/ie/archive/2006/01/23/516393.aspx
// 22.04.2006 progress indicator added.
// 28.01.2006 void return bug fixed again?
// 09.03.2006 enable late binding of prepare and finish methods by using an expression.
// 14.07.2006 Safari Browser Version 2.03/Mac OS X 10.4. compatibility: xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue
// 10.08.2006 date to xml format fixed by Kars Veling
// 16.09.2006 .postUrl -- unfinished...
// 26.11.2006 enable null for xml based objects
// 19.05.2007 enabling ajax engine calls with multiple parameters
// 14.07.2007 xml2json added.

// ----- global variable for the proxies to webservices. -----

/// <summary>The root object for the proxies to webservices.</summary>
var proxies = new Object();

proxies.current = null; // the current active webservice call.
proxies.xmlhttp = null; // The current active xmlhttp object.


// ----- global variable for the ajax engine. -----

/// <summary>The root object for the ajax engine.</summary>
var ajax = new Object();

ajax.current = null; /// The current active AJAX action.
ajax.option = null; /// The options for the current active AJAX action.

ajax.queue = new Array(); /// The pending AJAX actions.
ajax.options = new Array(); /// The options for the pending AJAX actions.

ajax.timer = null; /// The timer for delayed actions.

ajax.progress = false; /// show a progress indicator
ajax.progressTimer = null; /// a timer-object that help displaying the progress indicator not too often.

// ----- AJAX engine and actions implementation -----

///<summary>Start an AJAX action by entering it into the queue</summary>
ajax.Start = function (action, options) {
ajax.Add(action, options);
// check if the action should start
if ((ajax.current == null) && (ajax.timer == null))
ajax._next(false);
} // ajax.Start


///<summary>Start an AJAX action by entering it into the queue</summary>
ajax.Add = function (action, options) {
if (action == null) {
alert("ajax.Start: Argument action must be set.");
return;
} // if

// enable the late binding of the methods by using a string that is evaluated.
if (typeof(action.call) == "string") action.call = eval(action.call);
if (typeof(action.prepare) == "string") action.prepare = eval(action.prepare);
if (typeof(action.finish) == "string") action.finish = eval(action.finish);

if ((action.queueClear != null) && (action.queueClear == true)) {
ajax.queue = new Array();
ajax.options = new Array();

} else if ((ajax.queue.length > 0) && ((action.queueMultiple == null) || (action.queueMultiple == false))) {
// remove existing action entries from the queue and clear a running timer
if ((ajax.timer != null) && (ajax.queue[0] == action)) {
window.clearTimeout(ajax.timer);
ajax.timer = null;
} // if

var n = 0;
while (n < ajax.queue.length) {
if (ajax.queue[n] == action) {
ajax.queue.splice(n, 1);
ajax.options.splice(n, 1);
} else {
n++;
} // if
} // while
} // if

if ((action.queueTop == null) || (action.queueTop == false)) {
// to the end.
ajax.queue.push(action);
ajax.options.push(options);

} else {
// to the top
ajax.queue.unshift(action);
ajax.options.unshift(options);
} // if
} // ajax.Add


///<summary>Check, if the next AJAX action can start.
///This is an internal method that should not be called from external.</summary>
///<remarks>for private use only.<remarks>
ajax._next = function (forceStart) {
var ca = null // current action
var co = null // current opptions
var data = null;

if (ajax.current != null)
return; // a call is active: wait more time

if (ajax.timer != null)
return; // a call is pendig: wait more time

if (ajax.queue.length == 0)
return; // nothing to do.

ca = ajax.queue[0];
co = ajax.options[0];
if ((forceStart == true) || (ca.delay == null) || (ca.delay == 0)) {
// start top action
ajax.current = ca;
ajax.queue.shift();
ajax.option = co;
ajax.options.shift();

// get the data
if (ca.prepare != null)
try {
data = ca.prepare(co);
} catch (ex) { }

if (ca.call != null) {
ajax.StartProgress();

// start the call
ca.call.func = ajax.Finish;
ca.call.onException = ajax.Exception;
if ((data.constructor == Array) && (data.multi != null)) // 19.05.2007
ca.call.apply(ca, data);
else
ca.call(data);
// start timeout timer
if (ca.timeout != null)
ajax.timer = window.setTimeout(ajax.Cancel, ca.timeout * 1000);

} else if (ca.postUrl != null) {
// post raw data to URL

} else {
// no call
ajax.Finish(data);
} // if

} else {
// start a timer and wait
ajax.timer = window.setTimeout(ajax.EndWait, ca.delay);
} // if
} // ajax._next


///<summary>The delay time of an action is over.</summary>
ajax.EndWait = function() {
ajax.timer = null;
ajax._next(true);
} // ajax.EndWait


///<summary>The current action timed out.</summary>
ajax.Cancel = function() {
proxies.cancel(false); // cancel the current webservice call.
ajax.timer = null;
ajax.current = null;
ajax.option = null;
ajax.EndProgress();
window.setTimeout(ajax._next, 200); // give some to time to cancel the http connection.
} // ajax.Cancel


///<summary>Finish an AJAX Action the normal way</summary>
ajax.Finish = function (data) {
// clear timeout timer if set
if (ajax.timer != null) {
window.clearTimeout(ajax.timer);
ajax.timer = null;
} // if

// use the data
try {
if ((ajax.current != null) && (ajax.current.finish != null))
ajax.current.finish(data, ajax.option);
} catch (ex) { }
// reset the running action
ajax.current = null;
ajax.option = null;
ajax.EndProgress();
ajax._next(false)
} // ajax.Finish


///<summary>Finish an AJAX Action with an exception</summary>
ajax.Exception = function (ex) {
// use the data
if (ajax.current.onException != null)
ajax.current.onException(ex, ajax.option);

// reset the running action
ajax.current = null;
ajax.option = null;
ajax.EndProgress();
} // ajax.Exception


///<summary>Clear the current and all pending AJAX actions.</summary>
ajax.CancelAll = function () {
ajax.Cancel();
// clear all pending AJAX actions in the queue.
ajax.queue = new Array();
ajax.options = new Array();
} // ajax.CancelAll


// ----- show or hide a progress indicator -----

// show a progress indicator if it takes longer...
ajax.StartProgress = function() {
ajax.progress = true;
if (ajax.progressTimer != null)
window.clearTimeout(ajax.progressTimer);
ajax.progressTimer = window.setTimeout(ajax.ShowProgress, 220);
} // ajax.StartProgress


// hide any progress indicator soon.
ajax.EndProgress = function () {
ajax.progress = false;
if (ajax.progressTimer != null)
window.clearTimeout(ajax.progressTimer);
ajax.progressTimer = window.setTimeout(ajax.ShowProgress, 20);
} // ajax.EndProgress


// this function is called by a timer to show or hide a progress indicator
ajax.ShowProgress = function() {
ajax.progressTimer = null;
var a = document.getElementById("AjaxProgressIndicator");

if (ajax.progress && (a != null)) {
// just display the existing object
a.style.top = document.documentElement.scrollTop + 2 + "px";
a.style.display = "";

} else if (ajax.progress) {

// find a relative link to the ajaxcore folder containing ajax.js
var path = "../ajaxcore/"
for (var n in document.scripts) {
s = document.scripts[n].src;
if ((s != null) && (s.length >= 7) && (s.substr(s.length -7).toLowerCase() == "ajax.js"))
path = s.substr(0,s.length -7);
} // for

// create new standard progress object
a = document.createElement("div");
a.id = "AjaxProgressIndicator";
a.style.position = "absolute";
a.style.right = "2px";
a.style.top = document.documentElement.scrollTop + 2 + "px";
a.style.width = "98px";
a.style.height = "16px"
a.style.padding = "2px";
a.style.verticalAlign = "bottom";
a.style.backgroundColor="#51c77d";

a.innerHTML = "<img style='vertical-align:bottom' src='" + path + "ajax-loader.gif'>&nbsp;please wait...";
document.body.appendChild(a);

} else if (a) {
a.style.display="none";
} // if
} // ajax.ShowProgress


// ----- simple http-POST server call -----

ajax.postData = function (url, data, func) {
var x = proxies._getXHR();

// enable cookieless sessions:
var cs = document.location.href.match(/\/\(.*\)\//);
if (cs != null) {
url = url.split('/');
url[3] += cs[0].substr(0, cs[0].length-1);
url = url.join('/');
} // if

x.open("POST", url, (func != null));

if (func != null) {
// async call with xmlhttp-object as parameter
x.onreadystatechange = func;
x.send(data);

} else {
// sync call
x.send(soap);
return(x.responseText);
} // if
} // ajax.postData


///<summary>Execute a soap call.
///Build the xml for the call of a soap method of a webservice
///and post it to the server.</summary>
proxies.callSoap = function (args) {
var p = args.callee;
var x = null;
var paramsToPass = new Array();

for (var item in args[0]){

paramsToPass.push(args[0][item]);

}

// check for existing cache-entry
if (p._cache != null) {
if ((p.params.length == 1) && (paramsToPass.length == 1) && (p._cache[paramsToPass[0]] != null)) {
if (p.func != null) {
p.func(p._cache[paramsToPass[0]]);
return(null);
} else {
return(p._cache[paramsToPass[0]]);
} // if
} else {
p._cachekey = paramsToPass[0];
}// if
} // if

proxies.current = p;
x = proxies._getXHR();
proxies.xmlhttp = x;

// envelope start
var soap = "<?xml version='1.0' encoding='utf-8'?>"
+ "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>"
+ "<soap:Body>"
+ "<" + p.fname + " xmlns='" + p.service.ns + "'>";

// parameters
for (n = 0; (n < p.params.length) && (n < paramsToPass.length); n++) {
var val = paramsToPass[n];
var typ = p.params[n].split(':');

if ((typ.length == 1) || (typ[1] == "string")) {
val = String(paramsToPass[n]).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");

} else if (typ[1] == "int") {
val = parseInt(paramsToPass[n]);
} else if (typ[1] == "float") {
val = parseFloat(paramsToPass[n]);

} else if ((typ[1] == "x") && (typeof(paramsToPass[n]) == "string")) {
val = paramsToPass[n];

} else if ((typ[1] == "x") && (typeof(XMLSerializer) != "undefined")) {
val = (new XMLSerializer()).serializeToString(paramsToPass[n].firstChild);

} else if (typ[1] == "x") {
if (paramsToPass[n] != null)
val = paramsToPass[n].xml;

} else if ((typ[1] == "bool") && (typeof(paramsToPass[n]) == "string")) {
val = paramsToPass[n].toLowerCase();

} else if (typ[1] == "bool") {
val = String(paramsToPass[n]).toLowerCase();

} else if (typ[1] == "date") {
// calculate the xml format for datetime objects from a javascript date object
var s, ret;
ret = String(val.getFullYear());
ret += "-";
s = String(val.getMonth() + 1);
ret += (s.length == 1 ? "0" + s : s);
ret += "-";
s = String(val.getDate());
ret += (s.length == 1 ? "0" + s : s);
ret += "T";
s = String(val.getHours());
ret += (s.length == 1 ? "0" + s : s);
ret += ":";
s = String(val.getMinutes());
ret += (s.length == 1 ? "0" + s : s);
ret += ":";
s = String(val.getSeconds());
ret += (s.length == 1 ? "0" + s : s);
val = ret;

} else if (typ[1] == "s[]") {
val = "<string>" + paramsToPass[n].join("</string><string>") + "</string>";

} else if (typ[1] == "int[]") {
val = "<int>" + paramsToPass[n].join("</int><int>") + "</int>";

} else if (typ[1] == "float[]") {
val = "<float>" + paramsToPass[n].join("</float><float>") + "</float>";

} else if (typ[1] == "bool[]") {
val = "<boolean>" + paramsToPass[n].join("</boolean><boolean>") + "</boolean>";

} // if
soap += "<" + typ[0] + ">" + val + "</" + typ[0] + ">"
} // for
//var send = Ext.util.JSON.encode(p.params);
//soap += send;
// envelope end
soap += "</" + p.fname + ">"
+ "</soap:Body>"
+ "</soap:Envelope>";
//alert(soap);
// enable cookieless sessions:
var u = p.service.url;
var cs = document.location.href.match(/\/\(.*\)\//);
if (cs != null) {
u = p.service.url.split('/');
u[3] += cs[0].substr(0, cs[0].length-1);
u = u.join('/');
} // if

x.open("POST", u, (p.func != null));
x.setRequestHeader("SOAPAction", p.action);
x.setRequestHeader("Content-Type", "text/xml; charset=utf-8");

if (p.corefunc != null) {
// async call with xmlhttp-object as parameter
x.onreadystatechange = p.corefunc;
x.send(soap);

} else if (p.func != null) {
// async call
x.onreadystatechange = proxies._response;
x.send(soap);

} else {
// sync call
x.send(soap);
return(proxies._response());
} // if
} // proxies.callSoap


// cancel the running webservice call.
// raise: set raise to false to prevent raising an exception
proxies.cancel = function(raise) {
var cc = proxies.current;
var cx = proxies.xmlhttp;

if (raise == null) raise == true;

if (proxies.xmlhttp != null) {
proxies.xmlhttp.onreadystatechange = function() { };
proxies.xmlhttp.abort();
if (raise && (proxies.current.onException != null))
proxies.current.onException("WebService call was canceled.")
proxies.current = null;
proxies.xmlhttp = null;
} // if
} // proxies.cancel


// px is a proxies.service.func object !
proxies.EnableCache = function (px) {
// attach an empty _cache object.
px._cache = new Object();
} // proxies.EnableCache


// check, if a call is currently waiting for a result
proxies.IsActive = function () {
return(proxies.xmlhttp != null);
} // proxies.IsActive


///<summary>Callback method for a webservice call that dispatches the response to servive.func or service.onException.</summary>
///<remarks>for private use only.<remarks>
proxies._response = function () {
var ret = null;
var x = proxies.xmlhttp;
var cc = proxies.current;
var rtype = null;

if ((cc.rtype.length > 0) && (cc.rtype[0] != null))
rtype = cc.rtype[0].split(':');

if ((x != null) && (x.readyState == 4)) {
if (x.status == 200) {
var xNode = null;

if (rtype != null)
xNode = x.responseXML.getElementsByTagName(rtype[0])[0];

if (xNode == null) {
ret = null;

} else if (xNode.firstChild == null) { // 27.12.2005: empty string return values
ret = ((rtype.length == 1) || (rtype[1] == "string") ? "" : null);

} else if ((rtype.length == 1) || (rtype[1] == "string")) {
ret = xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue;

} else if (rtype[1] == "bool") {
ret = xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue;
ret = (ret == "true");

} else if (rtype[1] == "int") {
ret = xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue;
ret = parseInt(ret);

} else if (rtype[1] == "float") {
ret = xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue;
ret = parseFloat(ret);

} else if ((rtype[1] == "x") && (typeof(XMLSerializer) != "undefined")) {
ret = (new XMLSerializer()).serializeToString(xNode.firstChild);
ret = ajax._getXMLDOM(ret);

} else if ((rtype[1] == "ds") && (typeof(XMLSerializer) != "undefined")) {
// ret = (new XMLSerializer()).serializeToString(xNode.firstChild.nextSibling.firstChild);
ret = (new XMLSerializer()).serializeToString(xNode);
ret = ajax._getXMLDOM(ret);

} else if (rtype[1] == "x") {
ret = xNode.firstChild.xml;
ret = ajax._getXMLDOM(ret);

} else if (rtype[1] == "ds") {
// ret = xNode.firstChild.nextSibling.firstChild.xml;
ret = xNode.xml;
ret = ajax._getXMLDOM(ret);

} else if (rtype[1] == "s[]") {
// Array of strings
ret = new Array();
xNode = xNode.firstChild;
while (xNode != null) {
ret.push(xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue);
xNode = xNode.nextSibling;
} // while

} else if (rtype[1] == "int[]") {
// Array of int
ret = new Array();
xNode = xNode.firstChild;
while (xNode != null) {
ret.push(parseInt(xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue));
xNode = xNode.nextSibling;
} // while

} else if (rtype[1] == "float[]") {
// Array of float
ret = new Array();
xNode = xNode.firstChild;
while (xNode != null) {
ret.push(parseFloat(xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue));
xNode = xNode.nextSibling;
} // while

} else if (rtype[1] == "bool[]") {
// Array of bool
ret = new Array();
xNode = xNode.firstChild;
while (xNode != null) {
ret.push((xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue).toLowerCase() == "true");
xNode = xNode.nextSibling;
} // while

} else {
ret = xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue;
} // if

// store to _cache
if ((cc._cache != null) && (cc._cachekey != null)) {
cc._cache[cc._cachekey] = ret;
cc._cachekey = null;
} // if

var response = new Object();
proxies.xmlhttp = null;
proxies.current = null;
if (cc.argument){ response.responseText = ret;response.argument = cc.argument;};

if (cc.func == null) {
//return(ret); // sync
if (!cc.syncFunc){return(ret);}
else{cc.syncFunc(ret,cc.userContext);} // sync
return(null);
} else if(cc.argument){
cc.func(response); // async
return(null);
}
else {
cc.func(ret,cc.userContext); // async
return(null);
} // if

} else if (proxies.current.onException == null) {
// no exception

} else {
// raise an exception
ret = new Error();

if (x.status == 404) {
ret.message = "The webservice could not be found.";

} else if (x.status == 500) {
ret.name = "SoapException";
var n = x.responseXML.documentElement.firstChild.firstChild.firstChild;
while (n != null) {
if (n.nodeName == "faultcode") ret.message = n.firstChild.nodeValue;
if (n.nodeName == "faultstring") ret.description = n.firstChild.nodeValue;
n = n.nextSibling;
} // while

} else if ((x.status == 502) || (x.status == 12031)) {
ret.message = "The server could not be found.";

} else {
// no classified response.
ret.message = "Result-Status:" + x.status + "\n" + x.responseText;
} // if
proxies.current.onException(ret);
} // if

proxies.xmlhttp = null;
proxies.current = null;
} // if
} // proxies._response


///<summary>Callback method to show the result of a soap call in an alert box.</summary>
///<remarks>To set up a debug output in an alert box use:
///proxies.service.method.corefunc = proxies.alertResult;</remarks>
proxies.alertResult = function () {
var x = proxies.xmlhttp;

if (x.readyState == 4) {
if (x.status == 200) {
if (x.responseXML.documentElement.firstChild.firstChild.firstChild == null)
alert("(no result)");
else
alert(x.responseXML.documentElement.firstChild.firstChild.firstChild.firstChild.nodeValue);

} else if (x.status == 404) { alert("Error!\n\nThe webservice could not be found.");

} else if (x.status == 500) {
// a SoapException
var ex = new Error();
ex.name = "SoapException";
var n = x.responseXML.documentElement.firstChild.firstChild.firstChild;
while (n != null) {
if (n.nodeName == "faultcode") ex.message = n.firstChild.nodeValue;
if (n.nodeName == "faultstring") ex.description = n.firstChild.nodeValue;
n = n.nextSibling;
} // while
alert("The server threw an exception.\n\n" + ex.message + "\n\n" + ex.description);

} else if (x.status == 502) { alert("Error!\n\nThe server could not be found.");

} else {
// no classified response.
alert("Result-Status:" + x.status + "\n" + x.responseText);
} // if

proxies.xmlhttp = null;
proxies.current = null;
} // if
} // proxies.alertResult


///<summary>Show all the details of the returned data of a webservice call.
///Use this method for debugging transmission problems.</summary>
///<remarks>To set up a debug output in an alert box use:
///proxies.service.method.corefunc = proxies.alertResponseText;</remarks>
proxies.alertResponseText = function () {
if (proxies.xmlhttp.readyState == 4)
alert("Status:" + proxies.xmlhttp.status + "\nRESULT:" + proxies.xmlhttp.responseText);
} // proxies.alertResponseText


///<summary>show the details about an exception.</summary>
proxies.alertException = function(ex) {
var s = "Exception:\n\n";

if (ex.constructor == String) {
s = ex;
} else {
if ((ex.name != null) && (ex.name != ""))
s += "Type: " + ex.name + "\n\n";

if ((ex.message != null) && (ex.message != ""))
s += "Message:\n" + ex.message + "\n\n";

if ((ex.description != null) && (ex.description != "") && (ex.message != ex.description))
s += "Description:\n" + ex.description + "\n\n";
} // if
alert(s);
} // proxies.alertException


///<summary>Get a browser specific implementation of the XMLHttpRequest object.</summary>
// from http://blogs.msdn.com/ie/archive/2006/01/23/516393.aspx
proxies._getXHR = function () {
var x = null;
if (window.XMLHttpRequest) {
// if IE7, Mozilla, Safari, etc: Use native object
x = new XMLHttpRequest()

} else if (window.ActiveXObject) {
// ...otherwise, use the ActiveX control for IE5.x and IE6
try { x = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { }
if (x == null)
try { x = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { }
} // if
return(x);
} // proxies._getXHR


///<summary>Get a browser specific implementation of the XMLDOM object, containing a XML document.</summary>
///<param name="xmlText">the xml document as string.</param>
ajax._getXMLDOM = function (xmlText) {
var obj = null;

if ((document.implementation != null) && (typeof document.implementation.createDocument == "function")) {
// Gecko / Mozilla / Firefox
var parser = new DOMParser();
obj = parser.parseFromString(xmlText, "text/xml");

} else {
// IE
try {
obj = new ActiveXObject("MSXML2.DOMDocument");
} catch (e) { }

if (obj == null) {
try {
obj = new ActiveXObject("Microsoft.XMLDOM");
} catch (e) { }
} // if

if (obj != null) {
obj.async = false;
obj.validateOnParse = false;
} // if
obj.loadXML(xmlText);
} // if
return(obj);
} // _getXMLDOM


// convert complex XML structures to JavaScript objects (JSON)
proxies.xml2json = function (xObj) {
if (xObj.nodeType == 9)
return(xml2json(xObj.documentElement));

var n = xObj.firstChild;
if (n.nodeType == 3) {
// just a text node.
ret = n.nodeValue;

} else {
// a complex node.
var ret = { };

// analyse all subnodes
while (n != null) {
var nn = n.nodeName;
var nv = xml2json(n); // recursion !
if (ret[nn] == null) {
// maybe just a simple nested value
ret[nn] = nv;
} else if (ret[nn].constructor == Array) {
// nn is already an array, now with another value
ret[nn].push(nv);
} else {
// if more than 1 element with the same name is present
// an array is used to collect them all.
var tmp = new Array();
tmp[0] = ret[nn];
tmp[1] = nv;
ret[nn] = tmp;
} // if
n = n.nextSibling;
} // while
} // if
return(ret);
} // xml2json


///<summary>show the details of a javascript object.</summary>
///<remarks>This helps a lot while developing and debugging.</remarks>
function inspectObj(obj) {
var s = "InspectObj:";

if (obj == null) {
s = "(null)"; alert(s); return;
} else if (obj.constructor == String) {
s = "\"" + obj + "\"";
} else if (obj.constructor == Array) {
s += " _ARRAY";
} else if (typeof(obj) == "function") {
s += " [function]" + obj;

} else if ((typeof(XMLSerializer) != "undefined") && (obj.constructor == XMLDocument)) {
s = "[XMLDocument]:\n" + (new XMLSerializer()).serializeToString(obj.firstChild);
alert(s); return;

} else if ((obj.constructor == null) && (typeof(obj) == "object") && (obj.xml != null)) {
s = "[XML]:\n" + obj.xml;
alert(s); return;
}

for (p in obj) {
try {
if (obj[p] == null) {
s += "\n" + String(p) + " (...)";

} else if (typeof(obj[p]) == "function") {
s += "\n" + String(p) + " [function]";

} else if (obj[p].constructor == Array) {
s += "\n" + String(p) + " [ARRAY]: " + obj[p];
for (n = 0; n < obj[p].length; n++)
s += "\n " + n + ": " + obj[p][n];

} else {
s += "\n" + String(p) + " [" + typeof(obj[p]) + "]: " + obj[p];
} // if
} catch (e) { s+= e;}
} // for
alert(s);
} // inspectObj

// ----- End -----

amackay11
26 Sep 2007, 5:30 AM
Hi, just curious what issues you had with the web services? I've used xmlreader and HttpProxy with good success:


var xmlread = new Ext.data.XmlReader({ record: 'Table' },
// set up the fields mapping into the xml doc
['SchID','Date_sched','Planner','Descrip','Section','Date_modified'] );

dsgrid= new Ext.data.Store({
proxy: new Ext.data.HttpProxy({method:'POST',url: 'Service.asmx/getSchedules'}),
reader:xmlread});




[WebMethod]
public XmlDocument getSchedules(String dtstart, String dtend, String func, String rec)
{

XmlDocument xmlDoc = new XmlDocument();
DataSet ds = new DataSet();

// create a connection

SqlDataAdapter oda = new SqlDataAdapter("select * from blah", getConnString());

xmlDoc.LoadXml(ds.GetXml());
return xmlDoc;
}

DragonFist
26 Sep 2007, 6:10 AM
To be honest, I hadn't tried the httpProxy, so perhaps that works fine. Using the DataProxy I was able to get soap and couldn't directly read the JSON I was sending from the webservice. Same witht the treeloader.

Trying to find a solution, I had found other having similar problems:

here (http://extjs.com/forum/showthread.php?t=11561&highlight=asp.net+webservice)

and here (http://extjs.com/forum/showthread.php?t=8813&highlight=asp.net+webservice)

and here (http://extjs.com/forum/showthread.php?t=3196&highlight=asp.net+webservice)

I think it has more to do with making use of JSON via webservices. Also, as mentioned above, MS's javascript support only allows one XHR at a time and kills calls already started. I ran into this on a page that had several grids and couldn't figure out why only one random grid would populate at a time until I found out this is how MS set it up by design. So I wanted a set of proxies that would queue the calls, not kill all previous ones.

I simply figured after I worked out something that worked for me that I would share it and hope it might be of use to others.

gcorgnet
4 Oct 2007, 11:53 AM
Hi, just curious what issues you had with the web services? I've used xmlreader and HttpProxy with good success:


var xmlread = new Ext.data.XmlReader({ record: 'Table' },
// set up the fields mapping into the xml doc
['SchID','Date_sched','Planner','Descrip','Section','Date_modified'] );

dsgrid= new Ext.data.Store({
proxy: new Ext.data.HttpProxy({method:'POST',url: 'Service.asmx/getSchedules'}),
reader:xmlread});


[WebMethod]
public XmlDocument getSchedules(String dtstart, String dtend, String func, String rec)
{

XmlDocument xmlDoc = new XmlDocument();
DataSet ds = new DataSet();

// create a connection

SqlDataAdapter oda = new SqlDataAdapter("select * from blah", getConnString());

xmlDoc.LoadXml(ds.GetXml());
return xmlDoc;
}
Hi,

I am trying to use you sample code to call a WebMethod that takes two int arguments but I can't get it to work



var ds= new Ext.data.Store({
proxy: new Ext.data.HttpProxy({method:'POST',url: 'MathService.asmx/Add',
params:{a:1,b:2}
}),
reader:xmlread});


Any idea ?

dotnetCarpenter
4 Oct 2007, 12:13 PM
Can you post the Add method? Also check out, in Firebug, what the content-type of your requests is.

gcorgnet
4 Oct 2007, 12:23 PM
Hi,

Here is the add method (nothing fancy, you see ...)


[WebMethod]
public int Add(int a, int b) {
return a+b;
}
And here is my request header in Firebug



Request:Headers
Host:localhost:4553
User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7
Accept:text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language:en-us,en;q=0.5
Accept-Encoding:gzip,deflate
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive:300
Connection:keep-alive
X-Requested-With:XMLHttpRequest
Referer:http://localhost:4553/WebSite1/index.htm
Pragma:no-cache
Cache-Control:no-cache

dotnetCarpenter
4 Oct 2007, 6:52 PM
You need to install ASP.NET AJAX Extensions to get a JSON serializer. If you want Plain Old XML (POX) then you probably only need to set [WebServiceBinding(ConformsTo = WsiProfiles.None)]. You need to do that no matter what, because as all other AJAX library out there, Ext only supports REST web services.

Hi,
Here is the add method (nothing fancy, you see ...)


[WebMethod]
public int Add(int a, int b) {
return a+b;
}


You're missing the script method from the System.Web.Script.Services namespace (if ASP.NET AJAX Extensions is installed).


[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public int Add(int a, int b) {
return a+b;
}



And here is my request header in Firebug


Request:Headers
Host:localhost:4553
User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7
Accept:text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language:en-us,en;q=0.5
Accept-Encoding:gzip,deflate
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive:300
Connection:keep-alive
X-Requested-With:XMLHttpRequest
Referer:http://localhost:4553/WebSite1/index.htm
Pragma:no-cache
Cache-Control:no-cache

You haven't set the content-type. See this post:
An easy way of changing the AJAX request header (http://extjs.com/forum/showthread.php?t=5769&page=2)
Also you might want to tell ASP.NET what charset you want your data (use the content-type header as well).
If your host doesn't have ASP.NET AJAX then you can put the dll in your bin folder and it should work.

Happy coding!

amackay11
5 Oct 2007, 3:35 AM
gcorgnet,

If you look at your http://yourhost/Service.asmx/Add

You are probably getting the following returned :


<?xml version="1.0" encoding="utf-8" ?>
<int xmlns="http://tempuri.org/">3</int>

So there are two things I can suggest you do. Either format your result into an XML result that Ext xmlreader will understand OR use a 'Ext.data.Connection' and parse the results. I tried Ajaxpro, Atlas/Ajax Extensions etc... but have settled on web services/xml to keep things simple...

Option1 (no change to your web service, tested and works)

var AddResult= new Ext.data.Connection();
AddResult.request({
url: "Service.asmx/Add",
params: {a:1,b:2},
method: 'POST',
scope: this,
callback: function(options, success, response){
if (success){
var xml = response.responseXML;
var myresult= Ext.DomQuery.selectValue('int', xml, 0);
alert ('My result is ' + myresult);


}
}});


Option 2:
Modify your web service to something like (based on http://www.csharphelp.com/archives/archive199.html):

[WebMethod]
public XmlDocument Add(int a, int b)
{
XmlDocument xmldoc;
XmlNode xmlnode;
XmlElement xmlelem;
XmlElement xmlelem2;
XmlText xmltext;
xmldoc = new XmlDocument();
//let's add the XML declaration section
xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, "", "");
xmldoc.AppendChild(xmlnode);

xmlelem = xmldoc.CreateElement("", "rootnode", "");
xmldoc.AppendChild(xmlelem);
//let's add another element (child of the root)
xmlelem2 = xmldoc.CreateElement("", "myresult", "");
xmltext = xmldoc.CreateTextNode(Convert.ToString(a + b));
xmlelem2.AppendChild(xmltext);
xmldoc.ChildNodes.Item(1).AppendChild(xmlelem2);

return xmldoc;

}

and

var xmlread = new Ext.data.XmlReader({ record: 'rootnode' },
// set up the fields mapping into the xml doc
['myresult'] );

var ds= new Ext.data.Store({
proxy: new Ext.data.HttpProxy({method:'POST',url: 'MathService.asmx/Add' }),
reader:xmlread});
.
.
// pass params in ds.load call
ds.load({a:1, b:2});


As dotnetCarpenter said, Firebug is invaluable in figuring out what you send and what server is sending back.

gcorgnet
7 Oct 2007, 12:36 PM
Hi,

I thank you guys so much for your detailed answers.
I will test that today.

Thanks again

gcorgnet

gcorgnet
7 Oct 2007, 1:41 PM
Hi dotNetCarpenter,

Thanks for your advices.
I still have one problem though: I can't set the headers properly.
I am using Ext 1.1.1 with Ext base.

Here is the code I use :



var ds= new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
method:'POST',
params:{a:1,b:2},
url: 'MathService.asmx/Add',
headers:{"Content-Type":"application/json;charset=utf-8"}
}),
reader:xmlread});
and here is the error message from firebug:



System.InvalidOperationException: Request format is invalid: application/json;charset=utf-8.
at System.Web.Services.Protocols.HttpServerProtocol.ReadParameters()
at System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()
And my method is as follows:



[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public int Add(int a, int b) {
return a+b;
}
Is there something I am doing wrong ?

Thanks,

gcorgnet

gcorgnet
7 Oct 2007, 2:43 PM
Hi,

Just a precision, if I use this script to call method that doesn't take any argument, I don't have the error on the HTTP request.

One problem though, the response is sent in XML format (and not JSON) :



<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://tempuri.org/">Hello</string>

DragonFist
7 Oct 2007, 4:10 PM
Exactly.

This is why I wrote my own handlers as mentioned at the beginning of this thread.

Perhaps not the most elegant coding but they work for me and I get the json responses everytime.

There are apparently ways to do the same with JRock or AjaxPro as well. I didn't chase those down.

gcorgnet
7 Oct 2007, 4:22 PM
Thanks for the info DragonFist.
I'll look into it.

hazlema
7 Oct 2007, 9:42 PM
If you use .Net v3 you can use the new JSON seralizer:

public static class JsonSerializerClass
{
public static string ConvertToJSON(this object obj)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
Stream JSON = new MemoryStream();
serializer.WriteObject(JSON, obj);
JSON.Seek(0, 0);

StreamReader sr = new StreamReader(JSON);
return sr.ReadToEnd();
}

public static string ConvertToJSON(this object obj, string RootElement)
{
return "{" + string.Format("{0}:{1}", RootElement, obj.ConvertToJSON()) + "}";
}
}


Just be sure to descerate the classes you serialize like so:


[DataContract]
[Serializable]
public class Category
{
[DataMember]
public string ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public bool Visable { get; set; }

public Category()
{
Guid gen = Guid.NewGuid();
this.ID = gen.ToString("N");
}
public Category(string name)
{
this.Name = name;
this.Visable = true;

Guid gen = Guid.NewGuid();
this.ID = gen.ToString("N");
}
}


Then to call it you say:

this.PortfolioContent.Categories.ConvertToJSON("data");

or you can get sexy and add a bit of filtering

this.PortfolioContent.Categories.FindAll(c => c.Visable == true).ConvertToJSON("data");

callmevlad
18 Oct 2007, 7:25 PM
I was able to get ASP.net WebServices (with ASP.NET AJAX) working with Ext2.0 by adding a custom proxy class: (not sure if there's a better way to set the Content-Type ... any suggestions?)



Ext.data.AspNetAjaxHttpProxy = function(conn) {
Ext.data.AspNetAjaxHttpProxy.superclass.constructor.call(this);
this.conn = conn;
this.useAjax = !conn || !conn.events;

// This sets the header Content-Type which gets past the ASP.NET AJAX security mechanism
Ext.lib.Ajax.request = Ext.lib.Ajax.request.createInterceptor(function(method, uri, cb, data, options){
this.defaultPostHeader = "application/json; charset=utf-8;";
});
};

Ext.extend(Ext.data.AspNetAjaxHttpProxy, Ext.data.HttpProxy, {
load : function(params, reader, callback, scope, arg){
if(this.fireEvent("beforeload", this, params) !== false){

// This is the only line being added to make sure ASP.NET receives parameters
// in JSON format
params = Ext.util.JSON.encode(params);

var o = {
params : params || {},
request: {
callback : callback,
scope : scope,
arg : arg
},
reader: reader,
callback : this.loadResponse,
scope: this
};
if(this.useAjax){
Ext.applyIf(o, this.conn);
if(this.activeRequest){
Ext.Ajax.abort(this.activeRequest);
}
this.activeRequest = Ext.Ajax.request(o);
}else{
this.conn.request(o);
}
}else{
callback.call(scope||this, null, arg, false);
}
}
});


Here's an example creating a new Store by calling the web service that returns JSON:



Ext.onReady(function(){
var people = new Ext.data.Store({
proxy: new Ext.data.AspNetAjaxHttpProxy({
url: '/Services/PersonSearch.asmx/PerformFuzzyQuery'
}),


On the server-side, the Web Service is defined as follows:



[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.None)]
[ScriptService]
[GenerateScriptType(typeof(SearchResults))]
public class PersonSearch : WebService
{
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public SearchResults PerformFuzzyQuery(string query, int start, int limit)
{
...
}
}
}


The objects get converted to JSON automatically without any extra work, although I haven't tried it with more complex types.

DragonFist
18 Oct 2007, 11:01 PM
Good call.

I also got the treeloader to work by overriding the getParams method to JSON.encode the bp = this.baseParams
bp.node = node.id
bp = Ext.util.JSON.encode(bp)
return bp

For now, I also had to decode twice on the processResponse but I think there is a way to have the webservice not urlencode the response.