PDA

View Full Version : DirectProxy need work



mitchellsimoens
7 Mar 2011, 1:07 PM
So I discovered that ExtJS4 PR3 does not have a DirectProxy. Not sure why but nevertheless, I have made a quick version that is working for me. It's not the prettiest but it is working for me :D

I would invite people to fork my GitHub repo (https://github.com/mitchellsimoens/Ext.data.DirectProxy) and submit a pull request so that it can get better.

Once again, this only is working on my instance. I do not guarantee that it will work for everyone but that is why I put it on GitHub so that people can further it until Sencha goes ahead and makes it themselves.

Oh, and it's only working for the read action.

westy
8 Mar 2011, 2:07 AM
Cheers, I'll have a look.

westy
8 Mar 2011, 4:43 AM
Ok, first issue, although might be a non-issue if I'm wrong.

Isn't api meant to be optional, so can simply specify a directFn instead?

Anyway, if so then we get an error:
me.api is undefined
[Break On This Error] directFn = me.api[operation.action] || me.directFn


Should be a simple enough fix :)
me.api = me.api || {}; perhaps.

Oh, and apologies, I'm not that familiar with github.
I think I forked it (*snigger*), but I didn't appear to need to ask for a pull request or whatever.

Sorry, we use mercurial, and have used bazaar in the past; Git, and github in a particular, is a new one for me.

Cheers,
Westy

mitchellsimoens
8 Mar 2011, 4:50 AM
Yeah... do people set up a DirectProxy without an api? I have never just setup the proxy with just a directFn. Should be easy.

Git and SVN is pretty similar. Yes, I see you forked my project which is good. So after you push changes to your repo (it creates one for you when you fork), in order to get it into my repo (to keep a central places if other people decide to help) then at the top-right of your repo there should be a Pull Request button. It's up under the search field. That pull request will ask me to pull your changes into my repo.

Thanks for the help! Next up write support but that may be a little more in depth. Then again may not. If it helps you, I have been looking at the proxy n ExtJS3 to see how they have done it there. However, with the new data package, there are big changes and that's what has thrown me so far.

evant
8 Mar 2011, 4:56 AM
FYI this is being worked on, so I wouldn't put a great deal of effort into it.

mitchellsimoens
8 Mar 2011, 5:09 AM
FYI this is being worked on, so I wouldn't put a great deal of effort into it.

I figured. But couldn't let that hold me back. Creating a small app with a grid and a form but using Direct. Didn't think something this small would be a problem to use ExtJS4 right now.

westy
8 Mar 2011, 5:17 AM
Ahh, api should not be undefined, from looking at the Ext 3 docs.

It should be defaulted, probably by DirectProxy itself, to:
api: {
read : undefined,
create : undefined,
update : undefined,
destroy : undefined
}

directFn is used when you don't need full CRUD, as in our current case I guess.

Good to hear Evan is working on this though :)
Like he says, probably not worth putting too much effort in. Guess the idea should be to get something together so we can get data from the server, and wait for the official version... what you've come up with so far looks like it'll do that, so thanks for that!

Any issues like this can be worked around simply by adding the necessary config items.

So Evan, may as well fish a little since you're lurking, is pr4 due this Friday? Seems to be the release day at the moment.
Or maybe it's beta time?! Although I'd bet against that since beta is meant to be feature complete I believe, and there's still quite a few bits of the framework missing...

Can we expect any updates to the server-side stacks, or is that part of Direct considered static?

Cheers,
Westy

mitchellsimoens
8 Mar 2011, 5:22 AM
There were a few goals on why I went ahead and tried...


I new the data package changed so I wanted to take on this to learn of some changes.
Needed to get something reading data in a grid


Plus, the config code in the Store should be the same as what the official version will have so when the official one comes, we just have to remove the file I created and you are still going forward.

westy
8 Mar 2011, 5:42 AM
Yeah, it's excellent that we can get a head start on our client-server comms, and like you say, the config shouldn't have to change.

westy
8 Mar 2011, 5:55 AM
Hmm, find myself wondering whether Ext.Direct routers (i.e. server-side stacks) should now return Ext 4 code?

The issue I have is a store that's created when a file gets included (taken from an example), e.g. Namespace.stores.StoreName = new Ext.data.Store({...}) which references my Direct namespace.action.method in it's proxy.

This instance is instantiated before addProvider gets called (in an app.launch), and the fact the ashx include does not return code using Ext.define (I think) means that Ext.require will not work (again, another guess).

I can obviously re-jig how it works somewhat, but can't help but think that you should be able to require code returned from an Ext.Direct router.

Thoughts?


Edit: Easily sorted by setting proxy's directFn in beforeload, but my question still stands...

mitchellsimoens
8 Mar 2011, 12:32 PM
Found a bug in this proxy. If you set remoteSort and remoteFilter to true and sorters and filters when you first create a store it will load the store no matter if you set autoload to true or false. Easy way around this is to sort/filter when you go to load it. Not sure why it does this.

westy
9 Mar 2011, 3:21 AM
I'm still having issues getting up and running, currently trying to track down an infinite recursion issue.
No idea whether it's something to do with this proxy, or a framework issue at present, but thought would post here in case anyone has any ideas...

Have verified that my server-side works ok, returning an empty array at present, by calling it outside of the proxy (by calling direct router returned javascript, namespace.action.method(params, console.info)).

What I'm seeing starts with a call to Ext.direct.RemotingProvider#combineAndSend, this calls doSend, which calls into Ext.data.Connection#request, then Ext.data.Connection#setupOptions.
The problems then start, with trying to call Ext.encode(data), which calls doEncode, which is ultimately the function that called until Firefox pulls the plug...

The data in question contains my action, method, tid and type, all which look as expected.
The data.data array being passed to Ext.Encode has three elements:
0 - { serviceToken = "Foo", page=1, limit=25 }
1 - the function returned by DirectProxy's createCallback method by the looks of it
2 - the DirectProxy itself according to Illuminations

0 is my params, with the serviceToken being something I added using the proxy's extraParams property that will eventually have our authentication token in.
I'm guessing index 2 is the scope? Not sure why it's in the data array to be encoded...

It's very hard to step through to determine much more at this stage, but will keep at it :)


I don't think it's calling the service yet, since can't see anything in firebug.

I'll keep digging, and post back with any further insights...

westy
9 Mar 2011, 3:31 AM
Ahh, doRequest is returning the data array.

Is args.push(me.createCallback(trans, operation, callback, trans, scope), me) correct?

That's pushing me into the arguments array, where as it looks like the intention is to call the callback in the scope of me?

Guess I should look at the docs for doRequest to see what it's meant to return, so I'll do that now ;)

Edit: Hmm, docs say it's void, as is the read method, but ServerProxy's read returns whatever doRequest returns, so that doesn't seem right :/

westy
9 Mar 2011, 3:52 AM
Ok, if I remove that final 'me' it calls the service but posts up the following for the data array:
0 - my params
1- null

This responds with the message 'Parameters length does not match'.

So, think it's safe to say that it's the encoding of DirectProxy that causing the infinite recursion.
Quite why is another question perhaps...


If I call my direct method, erm, directly, as detailed before, the data array contains:
0 - Foo
1 - 1
2 - 25

This is because my interface is (string, long, long).
Perhaps my method should take an object, I'd certainly like to?!



Suppose this raises the question, what are server-side methods meant to look like when used with a store that supports paging, say?

Are you meant to have:
void Foo(long page, long limit, etc.)
or
void Foo(ServerProxyParams parameters) where ServerProxyParams contains the page, limit etc properties.

More of a question for the Ext dev team, but may as well continue my spamming of this thread since related :)

I think it'd prefer to pass an object for those that could be passed by ServerProxy internally, i.e:
pageParam = this.pageParam,
startParam = this.startParam,
limitParam = this.limitParam,
groupParam = this.groupParam,
sortParam = this.sortParam,
filterParam = this.filterParam,
dirParam = this.dirParam;

Would then, in my case have, void Method(string serviceToken, ServerProxyParams parameters), or maybe that'd be a single object, which is much nicer :)

Thoughts?

Cheers,
Westy

mitchellsimoens
9 Mar 2011, 4:21 AM
This is interesting. I have 2 stores loading using this proxy.

One that just loads users. No params or paging or anything on this so it's pretty simple.

The other has paging, remote sorting, remote filtering, and has a SearchField sending a search query. This store works as well except when I specify the sort and filtering in the config it will automatically load the store even though I don't want it to. Not sure why but the paging, sorting, filtering and searching all work as expected. Paging is using the PagingToolbar.

My request for the second store looks like this:


{
"action": "Calls",
"method": "getCalls",
"data": [
{
"limit": 100,
"sort": "[{\"property\":\"dateTime\",\"direction\":\"DESC\"}]",
"filter": "[{\"property\":\"isCompleted\",\"value\":0}]"
}
],
"type": "rpc",
"tid": 3
}

Which looks perfect. This is a request for when I searched for "birthday" and went to page 2:


{
"action": "Calls",
"method": "getCalls",
"data": [
{
"query": "birthday",
"start": 100,
"page": 2,
"limit": 100,
"sort": "[{\"property\":\"dateTime\",\"direction\":\"DESC\"}]",
"filter": "[{\"property\":\"isCompleted\",\"value\":0}]"
}
],
"type": "rpc",
"tid": 6
}

Once again, looks perfect. My response is like this (data is shortened as I'm not gonna post 100 results):


{
"type": "rpc",
"tid": 3,
"action": "Calls",
"method": "getCalls",
"result": {
"success": true,
"total": "5230",
"data": [
{
"id": "5262",
"takenById": "5",
"takenForId": "4",
"callerId": "5403",
"dateTime": "2011-02-25 17:13:00",
"description": "Birthdays for Sat. Feb. 26th. are Ginger Mays 272-0491, Curtis Brown of Road 261-6199 and Mon . Feb. 28th. Amanda Lopez of IT 279-5892 or 231-5122 and Kevin Major of Road 682-6695",
"dateTimeCreated": "2011-02-25 17:12:57",
"isCompleted": "0",
"callerFirstName": "Laura",
"callerLastName": "Hiingle",
"callerCompany": "",
"callerEmail": "",
"callerDateCreated": "2011-02-25 17:12:57",
"callerDateUpdated": "2011-02-25 17:12:57",
"callerHouseNo": "",
"callerAddress1": "",
"callerAddress2": "",
"callerCity": "",
"callerState": "",
"callerZip": ""
},
{
"id": "5261",
"takenById": "5",
"takenForId": "4",
"callerId": "5402",
"dateTime": "2011-02-25 16:21:00",
"description": "Looking for swampy land to ride ATV's on. He might add this to his span of areas for his swamp tours.",
"dateTimeCreated": "2011-02-25 16:20:47",
"isCompleted": "0",
"callerFirstName": "David",
"callerLastName": "Roscher",
"callerCompany": "ATV Swamp Tours",
"callerEmail": "",
"callerDateCreated": "2011-02-25 16:20:47",
"callerDateUpdated": "2011-02-25 16:20:47",
"callerHouseNo": "",
"callerAddress1": "",
"callerAddress2": "",
"callerCity": "",
"callerState": "",
"callerZip": ""
},
.....
]
}
}

I am using Kohana 3.1.1 on the backend using the Direct module that i found in the Ext: Direct forum for ExtJS 3.

Like I said, this is all working perfectly minus the forced loading.

westy
9 Mar 2011, 4:32 AM
Hmm, well I'd love my JSON to look like that... wonder why it doesn't?

I'm also using the PagingToolbar, that's why I added the page and limit params, since they're always passed. Was fully expecting to have to add more params as I get data into the grid and start playing with it...

Nothing special about my models or anything.

I'm using Evan's Ext.Direct router for .NET, but since it's not getting there yet this is all client code I think.

Will keep digging.

westy
9 Mar 2011, 4:59 AM
Interestingly your web service does have a single object parameter rather that each one separately.
I'll change mine, since prefer that shape anyway.

Will come back to this :)



Oh, what's your proxy config in your store, just to rule that out?
Mine:



proxy: {
type: 'direct',
api: {},
extraParams: {
serviceToken: 'Foo'
}
}


Edit: Oh, have I forgotton my directFn? LOL

Edit2: Oh no, forgot about an earlier post... am setting the directFn in beforeload since this is running before the call to addProvider.

Incidently, when are your stores created, and when are the providers hooked up?

westy
9 Mar 2011, 5:20 AM
Converting interface to an object makes data get passed up with a single element, Object { serviceToken="Foo", page=1, limit=25}.

It looks like the JavaScript returned by the router is inspected by Ext to see how many parameters are on the method, and an array built accordingly, hence my null in my latest case.

Still have no idea where the function and DirectProxy instances were coming from in array positions 1 and 2 earlier though...


Have a server-side direct issue now, but am close I think!

deemonas
9 Mar 2011, 5:28 AM
Hello,

In Ext 3.x I used this config:



proxy: new Ext.data.HttpProxy({
api: {
read : '/ajax/?action=read',
create : '/ajax/?action=create',
update: '/ajax/?action=update',
destroy: '/ajax/?action=destroy'
}
});

Now , when I try it in Ext 4 and your version:


proxy: {
type: 'direct',
url : '/ajax/',
extraParams : { ajax : 'testing'},
api: {
read : '/ajax/?action=read',
create : {url: '/ajax/?action=create'},
update: {url: '/ajax/?action=update'},
destroy: '/ajax/?action=destroy'
}
}

I see error: directFn.apply is not a function
chrome://firebug/content/blank.gifdirectFn.apply(window, args);

I had never used directFn before. I just need proxy to POST on specified in api URLs. Any suggestions ?

westy
9 Mar 2011, 5:42 AM
Hmm, my issue now is with the Ext.Direct router, so will take that discussion elsewhere.
Basically am passing up a JSON object, and have an class on my interface that matches the JSON content, and it's trying to directly cast the underlying dictionary (name/value pairs representing the JSON) to the class when making the call.

Somehow I thought the router, and JSON.net, were meant to sort that, parsing the JSON into the object instance. Maybe I'm missing a step, since surely that's kind of the point...

Anyway, thanks for the DirectProxy class Mitchell, has been invaluable for starting to understand how the new Ext 4 data stuff works. Coming from Ext 2 it's quite a change, but I think it's going to end up with a much cleaner solution than what we had before...

Cheers,
Westy

westy
9 Mar 2011, 5:47 AM
I see error: directFn.apply is not a function


In doRequest it assigns either me.api[operation.action] or me.directFn to a local variable called directFn.

Looks like it's expected read to now reference your direct function rather than a URL, since later it does directFn.apply(window, args);

So something like:


api: {
read: Namespace.action.method
}


Where the Namespace.action.method is as defined in the JavaScript returned by your direct router.

Hope that helps,
Westy

westy
9 Mar 2011, 7:48 AM
Progress... have written some server-side code to create and build an instance of an object parameter to the direct method in question.

Issue now is, since I've not put any data in my database, it's returning an empty array.
This manifests itself in the JSON as result: [].

This means result.success is false, which means I then get an error when the exception event is fired from DirectProxy.



createCallback : function(request, operation, callback, trans, scope) {
var me = this,
action = operation.action;

return function(result, req) {
if (!result.success) {
me.fireEvent('exception', me, 'remote', action, trans, res, null);
//...


Should res be req?

Cheers,
Westy

mitchellsimoens
9 Mar 2011, 7:52 AM
Hello,

In Ext 3.x I used this config:



proxy: new Ext.data.HttpProxy({
api: {
read : '/ajax/?action=read',
create : '/ajax/?action=create',
update: '/ajax/?action=update',
destroy: '/ajax/?action=destroy'
}
});

Now , when I try it in Ext 4 and your version:


proxy: {
type: 'direct',
url : '/ajax/',
extraParams : { ajax : 'testing'},
api: {
read : '/ajax/?action=read',
create : {url: '/ajax/?action=create'},
update: {url: '/ajax/?action=update'},
destroy: '/ajax/?action=destroy'
}
}

I see error: directFn.apply is not a function
chrome://firebug/content/blank.gifdirectFn.apply(window, args);

I had never used directFn before. I just need proxy to POST on specified in api URLs. Any suggestions ?

You are not using Direct properly. You are using more of a REST style.

westy
9 Mar 2011, 7:53 AM
I suspect when method being called returns null or an empty collection I should be getting:


result": {
"success": true,
"total": "0",
"data": [] // Or maybe no data element at all.
}


Sounds like a bug in the Direct router... will sort it.

mitchellsimoens
9 Mar 2011, 7:53 AM
Should res be req?

Probably. Forgot to touch the exception event. Wasn't an issue since mine is working :D

westy
10 Mar 2011, 2:06 AM
I suspect when method being called returns null or an empty collection I should be getting:


result": {
"success": true,
"total": "0",
"data": [] // Or maybe no data element at all.
}


Sounds like a bug in the Direct router... will sort it.

My assumption here was incorrect.

The Ext.Direct spec indicates that the result can be anything; it's simply the return from the method.

Looks like this proxy version differs from the v3 one in the fact that it checks the result object for a success member rather than checking the second parameter to the callback's (res) status member.
A small point but could mean that some rework is required when the official version hits.

westy
10 Mar 2011, 6:09 AM
Fairly fundamental bug fix for others that may be using this, although I understand it works fine for you Mitchell, although no idea how TBH, given this fix.

Change to createCallback:


createCallback : function(request, operation, callback, trans, scope) {
var me = this,
action = operation.action;

return function(result, res) {
if (!res.status) {
me.fireEvent('exception', me, 'remote', action, trans, res, null);
trans.request.callback.call(me, operation);
return;
}
if (action === "read") {
me.onRead(operation, action, trans, result, res, callback, scope);
} else {
me.onWrite(operation, action, trans, result, res, rs);
}
};
},



Fairly crucial bit, since original wanted a 'success' member in your result, and there should be no imposed members to what data we return. The 'res' object is part of how Ext.Direct works I believe.

Anyway, is now working for me, so I'm happy, can crack on with getting some real data flying about :)

Regards,
Westy

mitchellsimoens
10 Mar 2011, 6:13 AM
Fairly fundamental bug fix for others that may be using this, although I understand it works fine for you Mitchell, although no idea how TBH, given this fix.

Change to createCallback:


createCallback : function(request, operation, callback, trans, scope) {
var me = this,
action = operation.action;

return function(result, res) {
if (!res.status) {
me.fireEvent('exception', me, 'remote', action, trans, res, null);
trans.request.callback.call(me, operation);
return;
}
if (action === "read") {
me.onRead(operation, action, trans, result, res, callback, scope);
} else {
me.onWrite(operation, action, trans, result, res, rs);
}
};
},



Fairly crucial bit, since original wanted a 'success' member in your result, and there should be no imposed members to what data we return. The 'res' object is part of how Ext.Direct works I believe.

Anyway, is now working for me, so I'm happy, can crack on with getting some real data flying about :)

Regards,
Westy

Tested fix on my working Grids and it works so I pushed this change to my GitHub repo also. Great job and keep it up :-) Is everything working for you as far as reading goes?

westy
10 Mar 2011, 7:26 AM
Tested fix on my working Grids and it works so I pushed this change to my GitHub repo also. Great job and keep it up :-)
Excellent, and thanks :)


Is everything working for you as far as reading goes?
Just writing a script to put some test data in my database... should be done in the next half hour, then will try it.
I'm confident that the router and DirectProxy are sorted now though /fingers-crossed

Cheers,
Westy

westy
10 Mar 2011, 9:18 AM
Hmm, have data coming back now, but is slightly the wrong shape due to a JSON serialization issue I think.

Would expect an error of some kind in this case, but just seem to have a quarter height row on my grid with nothing in it... will investigate further tomorrow...

westy
11 Mar 2011, 6:54 AM
Just to complete the story, I'm now sorted and getting data populating in my grid :)

The final issue was the fact that JSON.Net was serializing the data coming back using the .NET class property names, which are Pascal case. Obviously I want the fields in my Ext model to be camel case.

Thankfully JSON.Net provides a CamelCasePropertyNamesContractResolver which you can add to your serializer settings, and then all is right with the world.

Now continuing to piece together my UI.

Cheers,
Westy

mitchellsimoens
11 Mar 2011, 6:55 AM
Awesome! Since I'm sure the official one will come soon, I don't see a need for myself to work on getting full CRUD working so I won't be working any more on it.

westy
14 Mar 2011, 7:12 AM
Not sure if this (http://www.sencha.com/forum/showthread.php?126752-PR3-Sorters-filters-and-group&p=580273#post580273) is of interest to you, but I've 'fixed' my version until I get a better suggestion :)

mitchellsimoens
16 Mar 2011, 6:32 AM
Using the official DirectProxy now. Works the same in my case. This thread should be closed but I can't edit in advanced mode :-|

mitchellsimoens
16 Mar 2011, 6:39 AM
Looks like official DirectProxy, like this one, will automatically sort. I am using remoteSort and specifying sorters and it will automatically load. No, not specifying autoLoad to true either.

mitchellsimoens
16 Mar 2011, 6:50 AM
Figured out how to close a thread without going advanced. Reply and one of the options is to close the thread! You have to be the OP to do this too.