PDA

View Full Version : use two grids displaying subset of same store



bobthebuilder
2 Dec 2009, 1:58 PM
Hello,

I'm trying to figure out the best architecture for the following problem:

I have one global store that contains a list of items and I want to then show two grids that have a different subset of this store. As I am showing both grids at the same time I can't use the filtering capabilities of the store as it will affect both.

I thought about duplicating the store but I want the grids to update whenever a record is updated in the store. This led me to think about having some sort of event chaining where the duplicated store knew about records being added to the parent store and updated itself. i.e. gets complicated quickly.

Anyone have any experience with this?

meercat
11 Jan 2011, 2:05 AM
Reviving an old thread (that got no replies) in case anyone has since had a smart idea about doing this...



I'm trying to figure out the best architecture for the following problem:

I have one global store that contains a list of items and I want to then show two grids that have a different subset of this store. As I am showing both grids at the same time I can't use the filtering capabilities of the store as it will affect both.

I thought about duplicating the store but I want the grids to update whenever a record is updated in the store. This led me to think about having some sort of event chaining where the duplicated store knew about records being added to the parent store and updated itself. i.e. gets complicated quickly.


I have much the same issue as Bob - two grid controls to be bound to the same store, but one grid only shows a subset where the other grid shows all items.
I've looked at various filter extensions but they all seem to still fundamentally apply the filter to the store where I want the filter to apply to the grid.
Short of re-writing grid, I figure I'd have to write a class ("ProxyStore"??) that copies the record types and entries from an existing store and listens for events (add/delete etc) on the original, but also pushes edits performed on itself back to the "original" store.

Has anyone done similar or got a better suggestion ?

--
Tim

siebmanb
21 Dec 2011, 9:31 AM
Up !

Same question here. My store stores "objects" and I have a list view with all of them and a view to display only one. How can I use my store "objects" in my view displaying only one "object" ?

Thank you

meercat
21 Dec 2011, 11:09 AM
I ended up writing a StoreView class that builds a store by cloning records from another store (note that Records can only belong to a single Store so my early attempts at sharing records b/n the true store and the view seemed somewhat doomed).

It clones the records on construction, and then listens to the original store for changes - in my case I don't need to push changes the other way the master is read/write, the view I only use as read-only, but it's not enforced by the code so beware).

It's not perfect but it works for me - code is below if it helps at all (I'm using underscore.js for iterators etc but hope it's clear enough what it's doing)

--
Tim -- @schmerg -- http://www.mysparebrain.com




//----------------------------------------------------------------------
// StoreView is a store that is a view on another Store
// Note that this store will update as the underlying store changes, but
// where you may want to reflect thing like the selection, the records in the
// two stores are different, so if using a Grid and a RowSelectionModel then you'll
// have to map thing appropriately.
//----------------------------------------------------------------------


// The mon() feature of adding listeners to another object but removing them when we're destroyed is
// rather handy but a feature of Ext.Component, so this adds the feature to another class
var addMonFeature = function(objOrClass) {
var p = Ext.isObject(objOrClass) ? objOrClass : objOrClass.prototype;
if (!Ext.isFunction(p.mon)) {
var cp = Ext.Component.prototype;
p.clearMons = cp.clearMons;
p.createMons = cp.createMons;
p.mon = cp.mon;
p.mun = cp.mun;


var prevPurgeFunc = p.purgeListeners;
if (Ext.isFunction(prevPurgeFunc)) {
p.purgeListeners = function() {
prevPurgeFunc.call(this);
if(this.mons){
this.on('beforedestroy', this.clearMons, this, {single: true});
}
};
}
}
};

//----------------------------------------------------------------------
// A StoreView is constructed with a store and a filter function, and builds a
// store that is a filtered view of the underlying store that updates as the underlying
// store updates too.
// Originally it was simply a store (and exposed the usual filter logic) but event handling
// gets complex when the underlying store adds rows etc so this, altho slightly less flexible,
// seems more robust
//
StoreView = Ext.extend(Ext.data.Store, {
// a store is not a component, so we override the ctor, not initComponent()
constructor : function(config){
// call parent constructor to make a new store with the same record type
// as the store we're viewing
StoreView.superclass.constructor.call(this, {
recordType: config.store.recordType
});
this.realStore = config.store;
this.filterFn = config.filter || function() { return true; };


// then clone all the existing records (a record can only be in one store at once)
this.reload();


// and add listeners for changes to the underlying store
var THIS = this;
this.mon(this.realStore, {
scope: this,
add : function( s, records, index ) {
// when new records are added, we only add those that
// pass th efilter
THIS.add(_.map( _.filter(records, THIS.filterFn),
function(r) {
var r2 = r.copy();
r2.original = r;
return r2;
}));
},
clear: function() {
this.removeAll();
},
datachanged: function(s) {
this.suspendEvents(false);
this.removeAll();
this.reload();
this.resumeEvents();
this.fireEvent("datachanged", this);
},
remove: function(s, r, index) {
// When records are removed from the underlying store, check
// to see if we should remove them too
var lr;
this.each(function(r2) {
if (r2.original == r) {
lr = r2;
}
});
if (lr) {
this.remove( lr );
}
},
save: function(s, batch, data) {
THIS.fireEvent("save", batch, data);
},
update: function(s, r, operation) {
this.suspendEvents(false);
this.removeAll();
this.reload();
this.resumeEvents();
this.fireEvent("datachanged", this); // tell controls to update themselves
}
});
},


reload: function() {
var records = [];
var f = this.filterFn;
this.realStore.each(function(r){
if (f(r)) {
var r2 = r.copy();
r2.original = r;
records.push(r2);
}
});
this.add(records);
},


realRecord: function(r) {
return r.original;
},


updateItem: function(r, opts) {
this.realStore.updateItem(
_.map( _.flatten([r]), function(r1) {
return this.realRecord(r1);
}, this),
opts );
}
});


addMonFeature( StoreView );