I had prewritten data access objects which I had to use for grid data binding. I found a good tutorial for this at gxt site at following link
http://www.extjs.com/helpcenter/index.jsp?topic=/com.extjs.gxt.help/html/tutorials/beanmodel.html
After I read the above I did following steps
1) Implement BeanModelTag in all beans. Following is an example of TimeZone bean which contains Timezone record from database.
public class TimeZone extends LightEntity implements Serializable, BeanModelTag
{
private int id;
private String name;
private String defaultValue;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
}
Note that it is serializable and it is marked as BeanModel by implementing BeanModelTag interface.
Next step is to fetch data from the database. My service implementation returns an ArrayList of above bean and it is as follows.
public class TimeZoneServiceImpl extends PersistentRemoteService implements
TimeZoneService {
public TimeZoneServiceImpl() {
super(HibernateUtil.getBeanManager());
}
@Override
public List
Session currentSession = HibernateUtil.getSessionFactory(false)
.getCurrentSession();
List
return timeZones;
}
}
You can call this service as a simple RPC call or you can use gxt RPCProxy which handles callback on its own. Using RPCProxy is easy and straight forward it will bind the grid columns with your beans properties using ColumnModel.
final TimeZoneServiceAsync timeZoneService = GWT
.create(DeviceService.class);
RpcProxy proxy = new RpcProxy() {
@Override
public void load(Object loadConfig, AsyncCallback callback) {
timeZoneService.getAllTimeZones(callback);
}
};
Abovel lines make an RPC call and fetch data from TimeZoneService. But we have not loaded this data yet and we have not provide any grid-data binding configuration. To load data into a store we need a loader, we can use ListLoader or PagingLoader etc. I used a ListLoader in this manner
BeanModelReader reader = new BeanModelReader();
ListLoader loader = new BaseListLoader(proxy, reader);
Here reader is used to handle creating new model instances from the beans being returned from the data proxy. So at the stage when you introduce reader into your code, your actual bean is gone and at runtime the gxt libaray creates its own instance of TimeZone class.
Now I have to create a store which can store the loaded data.
ListStore
So far what we have done is that we created a bean, created a proxy which brings a list of beans (filled with data), we created a reader which can read the returned beans, then we created a store and a loader, loader loads data into the store. But how it will bind the loaded data into the grid. This is done by ColumnModel.
ColumnConfig columnConfig=getColumnConfig();
ColumnModel cm = new ColumnModel(configs);
Following function creates binding by setting column id same as bean's properties. Here I would like to mention that while binding the grid column to bean's property it follows java's coding convention. Like the id of column name is "name" (column.setId("name"). While creating the binding at runtime that library find the getter method by concatenating get infront of id, in this example it would be getName(). If this getter is not found gxt will not raise any exception. It simply ignores this binding. For example if the name of getter was get_name() then your binding will not be done as expected. So be careful while creating these bindings.
private ColumnConfig getColumnConfig(){
ArrayList
ColumnConfig column = new ColumnConfig();
column.setId("name");
column.setHeader("Name");
column.setAlignment(HorizontalAlignment.CENTER);
column.setWidth(84);
config.add(column);
ColumnConfig column = new ColumnConfig();
column.setId("defaultValue");
column.setHeader("Default Value");
column.setAlignment(HorizontalAlignment.CENTER);
column.setWidth(84);
config.add(column);
}
After you make the binding your grid is ready to go.
To me gxt grids with their store API is robust solution. You can create grids with checkboxes, with multiple headers, with custome styles for each column data etc. They saved me a lot of time and efforts.
Shamaila! You rock, this helped me out perfectly. Lack of docs/seeming code complexity had me stumped but after reading your post I was up and running in just a few hours!
ReplyDeleteThanks-
John-
Thanks John for nice comments. Yeah this is really bad that we have such a great API but lack of proper documentation makes using it harder. I think if users write such blogs or write their experiences in relative forums it will help others. Also I have seen that most of the people nearly same problems while playing such APIs. If we post our problems and their solutions it will help creating good community and it will help others.
ReplyDeleteThanks for the great info. I am new to Gxt and have a doubt.
ReplyDeleteIf I have all my beans in an array and I dont need to use the RPC call, then how do I get the beans in my store such that it will work with grid? i.e. These 3 lines below will get replaced with what?
BeanModelReader reader = new BeanModelReader();
ListLoader loader = new BaseListLoafer(proxy, reader);
ListStore store = new ListStore(loader);
Thanks
Good Work Shamaila!
ReplyDeleteI was wondering if it would be possible for you to upload your project for others to use it as a skeletal architecture pattern?
thank you in advance
Hi arkitect,
ReplyDeleteI think I have already pasted here most of the code. You can copy them and create classes to start with. I will also try to upload a fully functional application to download for others.
Hi Shamaila. I need explicit control to the click events on the headers and extend the default behavior of column sorting to include secondary and tertiary level sorting. Did you happen to deal with this situation ?
ReplyDeleteI have not dealt with header events and sorting but I believe that you can handle Grid.HeaderClick event.
ReplyDeleteHeaderClick : GridEvent(grid, rowIndex, colIndex, event)
Fires a header is clicked.
grid : this
rowIndex : row index
colIndex : column index
event : the dom event
About custom sort I don't believe that it is possible in the current stage of GXT grid to implement a custom sort. I have two suggestions to implement the custom sort.
1) Handle header click event and do a server side sort meaning bring the sorted data from your database.
2) Extend Store and implement client side multi-level sorting. I have seen a possible solution at gxt forum I am pasting the solution here.
It doesn't look like Ext supports this. But you can work around it. This code adds a "customSort" config setting to Ext.data.Store. This is a function that takes two arguments: the Ext.data.Records to compare. It should return 1 if the first record is greater, -1 if the second is greater, or 0 if they're equal.
Ext.override(Ext.data.Store, {
sortData : function(f, direction){
direction = direction || 'ASC';
var fn = this.fields.get(f).customSort;
if (!fn) {
var st = this.fields.get(f).sortType;
var fn = function(r1, r2){
var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
};
}
this.data.sort(direction, fn);
if(this.snapshot && this.snapshot != this.data){
this.snapshot.sort(direction, fn);
}
}
});
To use it, add the customSort function to your field definitions. This example sorts a "street" field by state first, then city, then street itself.
new Ext.data.JsonReader(
{...},[
{name:'address', mapping:'street',
customSort:function(r1, r2) {
var v1 = r1.data["state"], v2 = r2.data["state"];
if (v1 !== v2) return v1 > v2 ? 1 : -1;
v1 = r1.data["city"], v2 = r2.data["city"];
if (v1 !== v2) return v1 > v2 ? 1 : -1;
v1 = r1.data["street"], v2 = r2.data["street"];
if (v1 !== v2) return v1 > v2 ? 1 : -1;
return 0;
}
}]
);
Hi Shamaila, can i have the complete source code of your implementation? I'm confused with my code because it won't show up the data to the grid.
ReplyDeleteHi Lynard,
ReplyDeleteAll the code is present in the example. If you are not getting data in your grid please use firbug to examin what data you are getting after your a fetch call is made. If you are getting right data then look into the bindings. If you want you can send me your code.
Thanks
Hi Guys, I have tried following the Shamaila's example, but I get no data on the grid. I have not used BeanFactory anywhere whatsoever. What could be wrong with my code?
ReplyDeleteHere is my code
--------------------------------------
RpcProxy proxy = new RpcProxy() {
@Override
protected void load(Object loadConfig, AsyncCallback callback) {
// TODO Auto-generated method stub
greetingService.getCars(callback);
}
};
BeanModelReader reader = new BeanModelReader();
// loader and store
ListLoader loader = new BaseListLoader(proxy, reader);
ListStore store = new ListStore(loader);
loader.load();
// column model
List columns = new ArrayList();
columns.add(new ColumnConfig("car_make", "Make", 200));
columns.add(new ColumnConfig("car_model", "Model", 100));
columns.add(new ColumnConfig("car_year", "Year", 50));
ColumnModel cm = new ColumnModel(columns);
Grid grid = new Grid(store, cm);
grid.setAutoExpandColumn("car_make");
grid.setWidth(400);
grid.setAutoHeight(true);
grid.setBorders(true);
contentPanel.setHeading("BeanModel Grid Example");
contentPanel.setSize(400, 200);
contentPanel.setLayout(new FitLayout());
contentPanel.add(grid);
----------------------------------------
Thanking you,
Simon.
Hi Simon,
ReplyDeleteIts been a while when I had used this grid. I cannot remember exactly but here are two suggestions
1) Please see if you have to call something like grid.setAutoLoad(true)?
2) Use firebug to see if your loaddata request is going to the server and bringing correct JSON data.
If you are still unable to fix the issue then send me the JSON response.
Hi Shamaila, thanks for the quick response!
ReplyDeleteUnfortunately, there is no such method as .setAutoLoad().
I have installed firebug, and this is the response.
------------------------------
//OK[2004,7,6,2,2004,5,3,2,2007,4,3,2,3,1,["java.util.ArrayList/3821976829","org.example.moi.client.Car/2809884344","Benz","GL","ML","Toyata","Carib"],0,5]
-----------
I see all my records from the server are shown.
Am not yet able to display the data on the grid.
Anything am missing?
Thanking you,
Simon.
Hi Simon
ReplyDeleteLooks like your JSON is not valid. For example see my JSON that I had got from the server
[
{
"id": 16,
"name": "sites package",
"description": "",
"isPrivate": false,
"rating": 3,
"dateCreated": {
"@class": "sql-date",
"$": "2011-06-05"
}
},
{
"id": 17,
"name": "russian package1",
"description": "",
"isPrivate": false,
"rating": 0,
"dateCreated": {
"@class": "sql-date",
"$": "2011-06-05"
}
}
}
]
The JSON starts and end with square brackets. GXT grid matches the grid column names with JSON element names and assign values accordingly. For example here I have id, name, description, rating etc as JSON object properties. In my grid there were columns with the same name, thus the grid find related values.
Hi Shamaila,
ReplyDeleteI was able to get my way around it. I followed the example at https://code.google.com/p/gaedemoiuqrul/source/browse/trunk/gwtSamples/gxtShowcase/src/com/extjs/gxt/samples/client/examples/grid/BeanModelGridExample.java?r=62 . I took off time, and wrote line by line, tested their code, and until it worked.
Thank you all for your time.
Simon.