This old title describes the visible issue, but the new title describes the root cause issue a little better. I'm still not sure why the ASHX isn't getting called, so I'm still looking for an answer. I have a workaround for now by hard coding the API descriptor (Ext.app.REMOTING_API variable). I got that JSON object (Ext.app.REMOTING_API variable) from the DirectProxy "provider" returned variable, from the original code running .NET 3.5. I just used an online JavaScript beautifier to make it look beautiful (so you can read it, instead of being on a single line).
Old Title: ExtJS 3 Ext.Direct proxy error: Uncaught TypeError: Cannot read property 'events' of undefined
I just upgraded the extdirect4dotnet project from .NET 3.5 to .NET 4.0. That project and a sample project can be found here https://code.google.com/p/extdirect4dotnet/. When I did this, I had to temporarily copy the Web.Config file when I changed the project version. I fixed the parameter list of two override methods that was due to inheriting a class in JSON.NET found at http://json.codeplex.com/SourceControl/latest#readme.txt. I also fixed the way DLL references were being made in both projects. I then replaced all occurances of "3.5" to "4.0" in the Web.Config.
Unfortunately, this project uses ExtJs 3.0.0, not ExtJs 4.2.* which is the framework version I'm implementing the Ext.Direct proxy for with this .NET server-side stack (extdirect4dotnet) with. So it still uses some old syntax. Below I'm trying to figure out how to fix a particular error after the .NET upgrade. It's still using ExtJs 3.0.0. See "see this line" in the "addProvider" function to see the line it's failing on. When I debug this from JavaScript, "provider" is undefined. I guess it doesn't know how to get provider Ext.app.REMOTING_API
after I upgraded this to .NET 4.0.
JavaScript error:
Uncaught TypeError: Cannot read property 'events' of undefined
ExtJs code:
/*!
* Ext JS Library 3.0.0
* Copyright(c) 2006-2009 Ext JS, LLC
* [email protected]
* http://www.extjs.com/license
*/
...
* </code></pre>
* @param {Object/Array} provider Accepts either an Array of Provider descriptions (an instance
* or config object for a Provider) or any number of Provider descriptions as arguments. Each
* Provider description instructs Ext.Direct how to create client-side stub methods.
*/
addProvider : function(provider){
var a = arguments;
if(a.length > 1){
for(var i = 0, len = a.length; i < len; i++){
this.addProvider(a[i]);
}
return;
}
// if provider has not already been instantiated
if(!provider.events){ // <========================= see this line
provider = new Ext.Direct.PROVIDERS[provider.type](provider);
}
provider.id = provider.id || Ext.id();
this.providers[provider.id] = provider;
provider.on('data', this.onProviderData, this);
provider.on('exception', this.onProviderException, this);
if(!provider.isConnected()){
provider.connect();
}
return provider;
},
Sample project JavaScript code:
Ext.Direct.addProvider(Ext.app.REMOTING_API);
Screen shot of JavaScript debugger on "provider" object with .NET 3.5 implementation (before .NET upgrade)
(open in new window to see a larger image)
As a workaround, I've done the following to my js file and hard coded the provider (Ext.app.REMOTING_API variable). This is essentially a JSON object which is my API Descriptor. I captured the variable "provider" from the inherited ASHX code below when the sample project was configured with .NET 3.5. For some strange reason, my Generic Handler in ASP.NET is not getting called. The class the Generic Handler is inheriting from seems like normal .NET 4.0 Generic Handler (ASHX) code to me. I've pasted the HTML, ASHX and inherited ASHX below.
Ext.onReady(function () {
Ext.app.REMOTING_API = {
"type": "remoting",
"id": "1",
"url": "../directRouter.ashx",
"actions": {
"CallTypes": [{
"name": "Echo",
"len": 1,
"formHandler": false
}, {
"name": "GetTime",
"len": 0,
"formHandler": false
}, {
"name": "UploadHttpRequestParam",
"len": 1,
"formHandler": true
}, {
"name": "UploadNamedParameter",
"len": 1,
"formHandler": true
}, {
"name": "SaveMethod",
"len": 3,
"formHandler": false
}, {
"name": "SaveMethod_Form",
"len": 1,
"formHandler": true
}, {
"name": "DateSample",
"len": 2,
"formHandler": false
}],
"TreeAction": [{
"name": "getChildNodes",
"len": 2,
"formHandler": false
}],
"CRUDSampleMethods": [{
"name": "create",
"len": 1,
"formHandler": false
}, {
"name": "read",
"len": 1,
"formHandler": false
}, {
"name": "update",
"len": 2,
"formHandler": false
}, {
"name": "destroy",
"len": 1,
"formHandler": false
}, {
"name": "reset",
"len": 0,
"formHandler": false
}]
}
};
Ext.Direct.addProvider(Ext.app.REMOTING_API);
//Ext.Direct.addProvider();
var Employee = Ext.data.Record.create([
{ name: 'firstname' }, // map the Record's "firstname" field to the row object's key of the same name
{name: 'job', mapping: 'occupation'} // map the Record's "job" field to the row object's "occupation" key
]);
var reader = new Ext.data.JsonReader({
totalProperty: 'results',
successProperty: 'success',
idProperty: 'id',
root: 'data'
}, [
{ name: 'id' },
{ name: 'email', allowBlank: false },
{ name: 'first', allowBlank: false },
{ name: 'last', allowBlank: false }
]
);
var writer = new Ext.data.JsonWriter({
returnJson: false,
writeAllFields: true
});
var store = new Ext.data.DirectStore({
api: {
read: CRUDSampleMethods.read,
create: CRUDSampleMethods.create,
update: CRUDSampleMethods.update,
destroy: CRUDSampleMethods.destroy
},
reader: reader,
baseParams: { dummy: 'blubb' },
writer: writer, // <-- plug a DataWriter into the store just as you would a Reader
paramsAsHash: true,
batchSave: false,
batch: false,
prettyUrls: false,
remoteSort: true,
listeners: {
load: function (result) {
},
loadexception: function () {
},
scope: this
}
});
//
var myPageSize = 10;
var userColumns = [
{ header: "ID", width: 40, sortable: true, dataIndex: 'id' },
{ header: "Email", width: 100, sortable: true, dataIndex: 'email', editor: new Ext.form.TextField({}) },
{ header: "First", width: 50, sortable: true, dataIndex: 'first', editor: new Ext.form.TextField({}) },
{ header: "Last", width: 50, sortable: true, dataIndex: 'last', editor: new Ext.form.TextField({}) }
];
Ext.onReady(function () {
Ext.QuickTips.init();
var userForm = new App.user.Form({
renderTo: 'user-form',
listeners: {
create: function (fpanel, data) { // <-- custom "create" event defined in App.user.Form class
var rec = new userGrid.store.recordType(data);
userGrid.store.insert(0, rec);
}
}
});
// create user.Grid instance (@see UserGrid.js)
var userGrid = new App.user.Grid({
renderTo: 'user-grid',
store: store,
columns: userColumns,
bbar: new Ext.PagingToolbar({
store: store, // grid and PagingToolbar using same store
displayInfo: true,
pageSize: myPageSize,
prependButtons: true,
items: [
'text 1'
]
}),
listeners: {
rowclick: function (g, index, ev) {
var rec = g.store.getAt(index);
userForm.loadRecord(rec);
},
destroy: function () {
userForm.getForm().reset();
}
}
});
setTimeout(function () {
Ext.get('loading').remove();
Ext.fly('loading-mask').fadeOut({
remove: true
});
store.load({ params: {
start: 0, // specify params for the first page load if using paging
limit: myPageSize,
foo: 'bar'
}
});
}, 250);
});
}); // onready
HTML code:
...
<link rel="stylesheet" type="text/css" href="../ext/resources/css/ext-all.css" />
<script type="text/javascript" src="../ext/ext-base-debug.js"></script>
<script type="text/javascript" src="../ext/ext-all-debug.js"></script>
<!-- directProxy.rfc was registred in the web.config -->
<script type="text/javascript" src="../directProxy.ashx"></script>
...
ASHX:
using System;
using System.Collections;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml.Linq;
using ExtDirect4DotNet;
namespace WebApplication1
{
/// <summary>
/// Zusammenfassungsbeschreibung für $codebehindclassname$
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class directProxy : DirectProxy
{
public directProxy()
{
DirectProxy.routerUrl = "../directRouter.ashx";
}
}
}
Inherited ASHX found within ExtDirect4DotNet (code didn't change during .NET 4.0 upgrade):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Configuration;
using System.Web.Configuration;
using ExtDirect4DotNet;
using ExtDirect4DotNet.helper;
namespace ExtDirect4DotNet
{
/// <summary>
/// Represents the Proxy For Ext.Direct Comunication.
/// Tha ProccessRequest Methode scanns all the available Assembly for Classes and Methods with the
/// Direct Attribute.
/// </summary>
public class DirectProxy : IHttpHandler
{
private static string url = "";
public static string routerUrl {
get { return url; }
set { url = value; }
}
public static DirectProvider getDirectProviderCache(string apiNameSpace)
{
string routerUrl = (DirectProxy.routerUrl == "") ? ConfigurationCache.getRouterUrl() : DirectProxy.routerUrl;
// set default namspace for the remoting API
string apiNamespace = (apiNameSpace == null || apiNameSpace == "") ? "Ext.app.REMOTING_API" : apiNameSpace;
DirectProviderCache cache = DirectProviderCache.GetInstance();
DirectProvider provider;
//After being configured, the provider should be cached.
if (!cache.ContainsKey(apiNamespace + "/" + routerUrl))
{
provider = new DirectProvider(apiNamespace, routerUrl);
provider.Configure(AppDomain.CurrentDomain.GetAssemblies());
if (!cache.ContainsKey(apiNamespace + "/" + routerUrl))
cache.Add(apiNamespace + "/" + routerUrl, provider);
}
else
{
provider = cache[apiNamespace + "/" + routerUrl];
}
return provider;
}
public void ProcessRequest(HttpContext context)
{
// set default namspace for the remoting API
string apiNamespace = "Ext.app.REMOTING_API";
if (context.Request.Form["ns"] != null)
{
// if there is an namespace parameter, use it...
apiNamespace = context.Request.Form["ns"].ToString();
}
DirectProvider provider = getDirectProviderCache(apiNamespace);
context.Response.Write(provider.ToString());
/*
old code..
// set the Response typ to javascript since the responce gets Parsed into an Script Tag
context.Response.ContentType = "text/JavaScript";
string rem = "{";
rem += "url: \""+routerUrl+"\",";
rem += "type:\"remoting\",";
//string json = DirectProxyGenerator.generateDirectApi();
//rem += json;
rem += "};";
rem = apiNamespace + ".REMOTING_API =" + rem;
rem = "(function(){" + rem + "})();";
context.Response.Write(rem);
*/
}
public bool IsReusable
{
get { return false; }
}
}
}