Proxies in Ext JS 4
February 2, 2011 42 Comments
One of the classes that has a lot more prominence in Ext JS 4 is the data Proxy. Proxies are responsible for all of the loading and saving of data in an Ext JS 4 or Sencha Touch application. Whenever you’re creating, updating, deleting or loading any type of data in your app, you’re almost certainly doing it via an Ext.data.Proxy.
If you’ve seen January’s Sencha newsletter you may have read an article called Anatomy of a Model, which introduces the most commonly-used Proxies. All a Proxy really needs is four functions – create, read, update and destroy. For an AjaxProxy, each of these will result in an Ajax request being made. For a LocalStorageProxy, the functions will create, read, update or delete records from HTML5 localStorage.
Because Proxies all implement the same interface they’re completely interchangeable, so you can swap out your data source – at design time or run time – without changing any other code. Although the local Proxies like LocalStorageProxy and MemoryProxy are self-contained, the remote Proxies like AjaxProxy and ScriptTagProxy make use of Readers and Writers to encode and decode their data when communicating with the server.
Whether we are reading data from a server or preparing data to be sent back, usually we format it as either JSON or XML. Both of our frameworks come with JSON and XML Readers and Writers which handle all of this for you with a very simple API.
Using a Proxy with a Model
Proxies are usually used along with either a Model or a Store. The simplest setup is just with a model:
var User = Ext.regModel('User', { fields: ['id', 'name', 'email'], proxy: { type: 'rest', url : '/users', reader: { type: 'json', root: 'users' } } });
Here we’ve created a User model with a RestProxy. RestProxy is a special form of AjaxProxy that can automatically figure out Restful urls for our models. The Proxy that we set up features a JsonReader to decode any server responses – check out the recent data package post on the Sencha blog to see Readers in action.
When we use the following functions on the new User model, the Proxy is called behind the scenes:
var user = new User({name: 'Ed Spencer'}); //CREATE: calls the RestProxy's create function because the user has never been saved user.save(); //UPDATE: calls the RestProxy's update function because it has been saved before user.set('email', 'ed@sencha.com'); //DESTROY: calls the RestProxy's destroy function user.destroy(); //READ: calls the RestProxy's read function User.load(123, { success: function(user) { console.log(user); } });
We were able to perform all four CRUD operations just by specifying a Proxy for our Model. Notice that the first 3 calls are instance methods whereas the fourth (User.load) is static on the User model. Note also that you can create a Model without a Proxy, you just won’t be able to persist it.
Usage with Stores
In Ext JS 3.x, most of the data manipulation was done via Stores. A chief purpose of a Store is to be a local subset of some data plus delta. For example, you might have 1000 products in your database and have 25 of them loaded into a Store on the client side (the local subset). While operating on that subset, your user may have added, updated or deleted some of the Products. Until these changes are synchronized with the server they are known as a delta.
In order to read data from and sync to the server, Stores also need to be able to call those CRUD operations. We can give a Store a Proxy in the same way:
var store = new Ext.data.Store({ model: 'User', proxy: { type: 'rest', url : '/users', reader: { type: 'json', root: 'users' } } });
We created the exact same Proxy for the Store because that’s how our server side is set up to deliver data. Because we’ll usually want to use the same Proxy mechanism for all User manipulations, it’s usually best to just define the Proxy once on the Model and then simply tell the Store which Model to use. This automatically picks up the User model’s Proxy:
//no need to define proxy - this will reuse the User's Proxy var store = new Ext.data.Store({ model: 'User' });
Store invokes the CRUD operations via its load and sync functions. Calling load uses the Proxy’s read operation, which sync utilizes one or more of create, update and destroy depending on the current Store delta.
//CREATE: calls the RestProxy's create function to create the Tommy record on the server store.add({name: 'Tommy Maintz'}); store.sync(); //UPDATE: calls the RestProxy's update function to update the Tommy record on the server store.getAt(1).set('email', 'tommy@sencha.com'); store.sync(); //DESTROY: calls the RestProxy's destroy function store.remove(store.getAt(1)); store.sync(); //READ: calls the RestProxy's read function store.load();
Store has used the exact same CRUD operations on the shared Proxy. In all of the examples above we have used the exact same RestProxy instance from three different places: statically on our Model (User.load), as a Model instance method (user.save, user.destroy) and via a Store instance (store.load, store.sync):
Of course, most Proxies have their own private methods to do the actual work, but all a Proxy needs to do is implement those four functions to be usable with Ext JS 4 and Sencha Touch. This means it’s easy to create new Proxies, as James Pearce did in a recent Sencha Touch example where he needed to read address book data from a mobile phone. Everything he does to set up his Proxy in the article (about 1/3rd of the way down) works the same way for Ext JS 4 too.
A question: Will ExtJS 4 allow one instance of Model belong to multiple Stores?
For example when I display one Model through stores in several Grids and update the Model, will all the Grids reflect the change accordingly?
In ExtJS 3 this wasn’t possible. A Record would only notify one Store of its changes – the one it was added most recently.
Multiple store support is not present yet but I’ve thought about adding it. There’s a good chance it will make it into 4.0
Yes, it will be great! One model and multiple stores is one thing I really need. It saves a lot of traffic. 🙂
Hi everyone! Ed thanks for the information, my question is Extjs 4, When? Thank you
Is any particular response expected from C, U, and D operations? Can the server reply with additional data that may not already be in the store? (In other words, can the server reply with merged updates that then replace the existing store content?)
@Michael you can use an Ext.data.Reader class to parse a custom response from the server. Store will read the data from your updates and apply them to itself. Proxy is the class that links the two together
Thanks for the great information.
I have an issue when syncing from an iPod Touch. I’m using a remote store with proxy set to type: ‘rest’, url: ‘service_forms’. When syncing from Safari on my Mac my app successfully POSTS to /service_forms/?_dc…… But when I post from my touch it is posting to /service_forms/1?_dc…..
The webhost (Ruby on Rails server) throws a RoutingError because no route matches “/service_forms/1” for POST. It almost seems like ExtJS is sending it as an update transaction (from the touch only) rather than a new transaction. But I’ve emptied out the web server database so it shouldn’t be seeing any records when creating the remote store. Do you know why it would be sending it to the “wrong” URL?
Thanks in advance
Excellent. I thought there might have been default behavior that if the response looks like the standard data load, it would be handled similarly. Thank you. Another question: is single record loading, directly from Models using RestProxy known to be broken in 1.0.1a? The id value is not appended to the request, even when using your example code verbatim. If not, are there any caveats I should know (i.e., any necessary preconditions between creating the Model and calling load)?
Pingback: ExtJS 4.0 Developer Preview – The draft overview guide | Coldfusion Developer
any body share about jsonp type ajax call for grid please?
Thanks,
Raghuram Reddy Gottimukkula
Hi Ed, thx for writing this useful article. But, unfortunately, u use json data inyour example. Wolud you like to give me example to load xml Data. thx
拜读博主的文章了,冒个泡。
Hey, Ed,
can the data be a mix of json and array, like the first part is json, and second part is array. which is something like this {col: [{nm: ‘c1′},{nm:’c2′},{nm:’c3’}], row: [{1, ‘r1c2’, ‘r1c3′}, {2,’r2c2′,’r2c3’},{…},…]}
if so, how can I read retrieve those data.
Thanks
@m101 Usually the easiest way is to just specify type: ‘auto’ for those fields in your model definition:
fields: [{name: ‘row’, type: ‘auto’}, {name: ‘col’, type: ‘auto’}]
This will leave the array unmolested and allow you to just access it like any other array
Pingback: extjstutorial.org
If I want to remove a record by store.remove(record), but I also want to execute some callbacks (e.g. success failure, check the HTTP status code), how would I do this? My situation is that I want to delete a record of a grid panel, however, the server side does some checks so see if my record can be deleted (it checks permissions and also record integrity – don’t delete if other records reference my record) – however, I can see no way to include these callbacks and the record is always removed from the grid even if the deletion failed (this can be checked simply by refreshing the store after deleting and seeing that the record is still in the grid). Any ideas?
Nice intro, but how does this fit in with forms:
Does one still use form.submit(), or does one have to go through other steps to use the model proxy methods?
Can success and failure functions be provided from the form save (however that should be done) to perform custom handling?
anyone can help me about ExtJS4 CRUD Basic grid with PHP and MySql, please….
Hi, I am testing out the functionallity in Ext4.
It seems to me that if a Store is configured with both a model and a proxy, then the store should use it’s own proxy, and not the model’s proxy.
Is it possible to set this up easily, eventually by subclassing the Store?
@Sun what you’ve described is correct – if you provide a Proxy directly to the Store it will use that Proxy, otherwise it will fall back to the Model’s Proxy (if it has one)
I provide a proxy directly to the Store in version 4.0.2a, but it doesn’t work the way you describe. But when I subclassed the Store like this, it works:
Ext.define(‘My.data.Store’, {
extend: ‘Ext.data.Store’,
constructor: function (config) {
this.callParent([config]);
if( this.proxy && this.model )
this.model.setProxy(this.getProxy());
}
});
Hey Ed,
Thanks for the post. There are a few things that remain unclear to me about ExtJS’s models, proxies and stores.
Is there a way to update a record (Model), and have a success and failure callback?
I’m trying to update a model which has a proxy. Calling set() doesn’t run the update method, but only calling sync() on the store does, however sync doesn’t allow for callbacks.
Can you point me in the right direction?
Thanks!
Hi,
I have a grid which allows user to create, destroy and update records locally and then when the user clicks “Save” buttons, the store.sync() function is called. Now there is a POST request for each type of modification in the table (create, destroy and update). How can I distinguish these POST requests from the client at server side so as to be able to make decisions and save on server accordingly.
e.g. the user deletes a record and then adds a new one, when he/she saves, there are 2 POST requests each carrying a json string but no way to distinguish which POST corresponds to which action.
Anyone have an idea about this?
Thanks,
Farish
Hi All,
I am using MemoryProxy with XML data. The memory proxy is then used by number of store, the Ext.data.XmlReader is used to parse the XML and load the ListView control. However, the ListView is not loaded.
My requirement:-
1] One memory proxy with XML data.
2] Multiple stores which uses the memoryproxy and each of the stores have reader(Ext.data.XmlReader).
Can anyone help me with a sample?
Thank you,
Suraj
Does anyone have an example of a proxy extension for .net web services calls?
@Devon calling .set is always a local function and can be undone before committing that change via the Proxy. Adding callbacks to the .sync method is on the list for 4.1 – until then you can do a somewhat less pleasant workaround overriding Store’s onProxyWrite function, which is called after every write operation. This is totally ridiculous and I’m sorry we didn’t have a better solution to that at launch time
Could you explain how to use the data sent to server?
with php, $_POST, $_GET, $_REQUEST does not show anything????
http://stackoverflow.com/questions/6702490/how-to-use-extjs4-save-in-php-server
Hi,
I’m using Sencha Touch and I’ve tried running your example regarding creating a new entity and posting it to my server, but for some reason, the call is always to /users/undefined and not to /users.
another issue I see is .load() with an ID does not work, it just goes to /users and not /users/{id} like it should.
what am I doing wrong?
Hi, Ed,
The proxy server at ExtJS 4.0.2a does not perform changed record commits at “processResponse” (I am using DirectStore with JsonReader + JsonWriter).
But I have just seen the source code online, through the new API docs… and Server.js version there is different from my 4.0.2a GPL version… I have noticed that there is now a Ext.data.Operation.commitRecords method, which performs the commits…
Should I code a workaround or wait for the next GPL release?
Thanks for your great work.
@J Bruni: that went into one of the later patch releases and will be available once 4.1.0 hits. I suggest you put a workaround in place via an Ext.override until 4.1.0 is out, then upgrade to either 4.1.0 or 4.0.7
Hi, on the store.sync(), how can i do a callback without setting it on the store itself?
Ahm… how can be managed the server fail on a delete/destroy request??
@jose in 4.1 we’ve added success/failure/callback options to each call to store.sync(), feels a lot better this way
It would be very interesting to have a proxy for the Stomp protocol, especially over websockets, which would allow for clean asynchronous push from the server and allow store management in a completely decoupled manner. REST/CRUD semantics would likely need to be supported within the messages themselves, but it would be very elegant to be able to just route these kinds of messages to client queues and have them magically (albeit transactionally) get updated.
To my previous comment, after looking at it some more, queueing Ext.data.Operation is probably what needs to happen here.
@Larry
even I am working on an app that expects response form .NET web service.
Please tell me if you came up with a solution?
I am getting response in XML.
Thanks
I finally found my problem, its not json version.
This may seem stupid, but I was working locally on my Desktop and I was doing a Json request to the server (www.domain.com/json.php).
You can create your interface without been on server. But if you use form and submit.
Your website must also be on a server.
I need some help with using proxies to get the PhoneGap Camera API working with my Sencha-touch code.
Currently, I am calling the navigator.camera.getPicture() function from my controller but nothing happens.
Will I require the use of proxies to access the camera API? Is there any place I can find some documentation?
@Anirudh you’re probably best off posting on the sencha.com forums so the community can help you out here
Hi,
I am using ExtJS4+Spring, I created one Contacts grid with Rest proxy type.
It allows the user to perform the create, destroy and update records locally.
And when user clicks the “Save” botton then im calling the store.sync() function,
and mapping this request in spring controller like
@RequestMapping(value=”/save.action”,method = RequestMethod.POST)
public @ResponseBody Map save(@RequestParam Object data)
How can I distinguish these POST requests from the client at server side so as to be able to make decisions and save on server accordingly.
Kindly suggest me.
Thanks,
Raja
All of the images on this page are broken.
Pingback: Extjs Store - Khai's personal knowledge vault.