PDA

View Full Version : Prevent duplicate rows in grid



infernoz
13 Mar 2012, 9:28 AM
Hello,

I have a model, store, and grid which is editable by the user of my application. When the user submits the data, I sync the grid up with my server side to do processing. The server side then returns to the client (store proxy reader) the exact rows that were sent to it. The model specifies an id column.

What I was expecting was the rows being returned should update the rows already existing in the store/grid with the data coming back if the ids match each other. Instead, additional rows are added to the store/grid, producing duplicate rows with the same id. (Ex: If I have 5 rows in the grid, update 2 of them and hit submit, 2 additional identical rows are added to the grid and I have 7 rows now. I want 5 rows only)

I would like the data coming from the server to update the rows with identical ids. If the id doesnt exist in the store, only then should a row be added. How can this be achieved?


Ext.define('DMT.model.PersonGeoUnitModel', {
extend: 'Ext.data.Model',

idProperty: 'id',

fields: [
{name: 'id', type: 'string'},
{name: 'ssoId', type: 'string'},
{name: 'ssoType', type: 'string'},
......
......
});


Ext.define('DMT.store.PersonGeoUnitStore', {
extend: 'Ext.data.Store',
model: 'DMT.model.PersonGeoUnitModel',
alias : 'widget.PersonGeoUnitStore',
storeId: 'personGeoUnitStore',

proxy: {
type: 'ajax',
url: '/DMT/index.html',
reader: {
type: 'json',
root: 'data'
},
writer: {
type: 'json',
root: 'data',
encode: true,
writeAllFields: true
},
extraParams: {'processorType': 'orgPersonGeoUnit'}
},

data: [
{id: 'abc_def-LICENSE-COUNTRY-SG-11/12/2011 00:00:00', ssoId: 'abc_def', ssoType: 'SSO', ........,.......,.......additional params},


Ext.define('DMT.view.PersonGeoUnitView' ,{
extend: 'Ext.grid.Panel',
alias : 'widget.personGeoUnitView',
id: 'personGeoUnitView',
title : 'Person Geo Unit Update',
store: 'PersonGeoUnitStore',
selType: 'cellmodel',

autoScroll: 'true',
layout: 'fit',

initComponent: function() {
Ext.apply(this, {
plugins: [{
ptype: 'cellediting',
clicksToEdit: 1
}],

columns: [{header: 'ID', dataIndex: 'id', hidden: true, flex: 1, hideable: false},
{header: 'SSO_ID', dataIndex: 'ssoId', flex: 1, hideable: false},
{header: 'SSO_TYPE', dataIndex: 'ssoType', flex: 1, hidden: true, hideable: false},
......
.......
............
],
dockedItems: [{
xtype: 'toolbar',
dock: 'top',
ui: 'footer',
layout: {
pack: 'left'
},
items: [
{ xtype: 'component', flex: 1 },
{ xtype: 'button', text: 'Submit', width: 80, listeners: {
'click': function() {
var personGeoUnitStore = Ext.data.StoreManager.lookup('PersonGeoUnitStore');
var response = personGeoUnitStore.sync();
}}}
]
}]
});

this.callParent(arguments);
}
});

Farish
13 Mar 2012, 9:42 AM
you should add a field of type int which has a unique value for each record and use this as the idProperty. When a new record is created, it is assigned id of 0. when u sync your store, these records are sent with "create". if an existing record is changes, the dirty parameter of that record is set to true. records with non-zero ids and dirty true (and phantom: false) are sent as "update".

infernoz
13 Mar 2012, 9:56 AM
Thanks for the response.

The dirty record piece makes sense, but what I dont understand is the interger id piece. I already have an 'id' column that is a string and will be unique for each record. Why is that not being used to show uniqueness? Does it have to be an integer to work?

Farish
13 Mar 2012, 9:56 AM
I am not 100% sure but I think that should solve your problem. without having an int id field, the store doesnt seem to work.

dedoz
13 Mar 2012, 10:16 AM
post the http response (data sent from server back to the client), or just check yourself if this data comes with clientId property.
this is a common mistake that produces the behavoir you explained.

infernoz
13 Mar 2012, 12:28 PM
Here is the response from the server. I've changed the numbers and names below to mask the data but you can get the point. What do you mean by "clientId"? Does one of the fields need to be named clientId? From the sencha docs I thought the default defined is "id". Also I explicitly set the idProperty on the model to "id"



{'data':[{"id":"E1000024162-SP-CNAME-12/23/2011 12:01:48",
"gsDataSourceId":"BLAH",
"updateId":"abc_def",
"startDate":"12/23/2011 12:01:48",
"endDate":null,
"updateDate":"03/13/2012 13:06:57",
"deleteRow":false,
"sId":"1234",
"aId":"2345",
"bId":"AB56",
"cId":"76589",
"dId":"234565422",
"eId":"48643",
"fId":"89439209",
"idType":"SP",
"statCode":"CNAME",
"statValue":"ABC DEF"}] ,'total':1,'success':true}

dedoz
13 Mar 2012, 12:49 PM
im goin to paste my post from another forum :d hope this helps you

you guys are missing the clientId in the response.
while you have a store with phatom records (records that doesnt exists yet in the dabatabase) a store.sync sends them with a clientId, as at this moment he doesnt know their id it uses this to recognize them after you respond.
so its like

{ newrecord : { id : unknown, name:'somename' }, clientId : 34534535 } ----> from client to server
<--- from server to client { newrecord : { id:33, name:'somename' }, clientId : 34534535 }
then Extjs know that record with id:33 references the phantom record with clientId 34534535
so he says "now record with clientId 34534535 has id : 33'

look at this


pStore = Ext.create('PersonStore');
pStore.add(Ext.create('Person',{name:'peter'}));
pStore.add(Ext.create('Person',{name:'mary'}));
pStore.add(Ext.create('Person',{name:'alex'}));
pStore.sync();

// HTTP REQUEST
//{"action":"testQuery","method":"personSelect",
// "data":[[
// {"someId":0,"name":"peter","clientId":"ext-record-1"},
// {"someId":0,"name":"mary","clientId":"ext-record-2"},
// {"someId":0,"name":"alex","clientId":"ext-record-3"}
//]],"type":"rpc","tid":1}

// RESPONSE
// {"type":"rpc","tid":1,"action":"testQuery","method":"personInsert",
// "result":[
// {"id":"1","name":"peter","clientId":"ext-record-1"},
// {"id":"2","name":"mary","clientId":"ext-record-2"},
// {"id":"3","name":"alex","clientId":"ext-record-3"}
// ]}

btw clientId is not a Person Model field , extjs add this to recognize them while goin to the server and coming back.
so your response should include them, usually while creating new record, your server side function just should modify the model id field in the given request, and send back that, the same request just with the id modified.

infernoz
13 Mar 2012, 1:20 PM
Does the name of the field have to be clientId? I dont see anything in the sencha docs regarding this.

Also, thought the records I am setting are phantom records, they are set with ids prior to the sync call. The ids that are being returned after the sync are identical to the ones that are placedon the records before the sync.

I took a very careful look at the request being sent to the server and there is no clientId being added as a parameter either. How would one add this to the request/ response if it is not added by ExtJS, and not part of the Person Model?

infernoz
13 Mar 2012, 1:40 PM
FYI also, making the id an integer did not solve this issue.

vietits
13 Mar 2012, 3:57 PM
It seems that your store generates creating request instead of updating request though you edit the existing records only.

I don't think the type of idProperty field is a matter. It can be a string or a number provided that it is unique.

Also, with original implementation of Ext 4, it does not use clientId. The store assumes that returned records for each creating/updating/destroying request are in order of how they were sent.

dedoz
13 Mar 2012, 6:23 PM
Does the name of the field have to be clientId? I dont see anything in the sencha docs regarding this.
our mistake was not carify which version of ext are you using :D im using 4.1 beta 1 or beta 2, or beta 3. clientId property is on data/model documentation of those versions (not on the online docs which correspond to 4.0.7. Which version of Ext are you using ? if you are using a 4.1 version , check the docs that comes in the downloaded folder, in data / model docs find the clientId config.

vietits
13 Mar 2012, 8:50 PM
@dedoz,

You're right, Ext 4.1 uses clientId. I am using Ext 4.0.7 and this version does not use clientId.

infernoz
14 Mar 2012, 8:04 AM
Sorry should have mentioned my version, I'm using 4.0.7.

How would you differentiate a creating request from an updating request when sending data back to the client from the server?

dedoz
14 Mar 2012, 11:57 AM
i cant help you there D: havent use ajax proxy , just direct proxy where is clear the requested action (read, create, insert,delete) and in the http requests appears as "read" or "create" etc.

post your http request maybe theres a property that indicates the action.
other than that maybe vietits can help you :d

infernoz
14 Mar 2012, 12:43 PM
The request consists of a data parameter that is in json format and the parameter processorType which is an extra param tat I defined. There is also a _dc param that has to do with caching on the client side, I believe. Nothing else.



data
[{"id":...............}]




processorType


orgPersonGeoUnit

dedoz
14 Mar 2012, 2:39 PM
reading bout ajax proxy, he makes diferences on requests using http verb as restful action.
so u have a configured url for your proxy (the server side code that process your client side to select, insert update and delete records from the database) is it php?

anyway, the diferences between create and update request in restful is known by the http request verb (or "method")
theres POST GET PUT and DELETE
so to create a new record will send those params you described trough a POST request
to update record will use PUT method.

so check in you http request the method used, it is a PUT or POST ?

btw you can check info for this in ajax proxy docs
http://docs.sencha.com/ext-js/4-0/#!/api/Ext.data.proxy.Ajax-property-actionMethods
which has a link for
http://docs.sencha.com/ext-js/4-0/#!/api/Ext.data.proxy.Rest
(http://docs.sencha.com/ext-js/4-0/#!/api/Ext.data.proxy.Rest)
have you tried direct proxy ? hehe
it has a nice api that can end like


api: {
read : person.Select,
create : person.Insert,
update : person.Update,
destroy : person.Delete
}

those person.methods are server side methods that will be called according to the operation on a person model

vietits
14 Mar 2012, 3:40 PM
To differentiate what the request is you should catch the beforesync of the store and then check the sync options. This is a hash of all records to be synchronized, broken down into create, update and destroy.


Ext.define('DMT.store.PersonGeoUnitStore', {
extend: 'Ext.data.Store',
model: 'DMT.model.PersonGeoUnitModel',
alias : 'widget.PersonGeoUnitStore',
storeId: 'personGeoUnitStore',
...
listeners: {
beforesync: function(options){
console.log(options);
}
}

infernoz
15 Mar 2012, 1:00 PM
Thanks everyone, havent had a chance to try this out to see what type of request is being sent. Will attempt to take a look tomorrow and let you know.

I am using Java Servlets as my server side btw.

infernoz