PDA

View Full Version : A remote storage priovide for state management



kesteb
21 Jul 2011, 7:11 AM
I am experimenting with the state management stuff and decided that the provided CookieProvider and LocalStoarageProvider were rather limiting. So I developed a RemoteStorageProvider that uses a store to manage the interactions. Here is the code:



/*
* File: RemoteStorageProvider.js
* Date: 20-Jul-2011
* By : Kevin L. Esteb
*
* This module provides a state provider that uses remote storage as
* the backing store.
*
* GNU General Public License Usage
*
* This file may be used under the terms of the GNU General Public
* License version 3.0 as published by the Free Software Foundation.
* Please review the following information to ensure that the GNU
* General Public License version 3.0 requirements will be met:
* http://www.gnu.org/copyleft/gpl.html.
*
*/

Ext.define('Ext.ux.state.RemoteStorageProvider', {
extend: 'Ext.state.Provider',
mixins: {
observable: 'Ext.util.Observable'
},
uses: [
'Ext.data.Model',
'Ext.data.Store',
'Ext.state.Provider',
'Ext.util.Observable'
],

store: {},

constructor: function(config) {

config = config || {};
this.initialConfig = config;

Ext.apply(this, config);

this.mixins.observable.constructor.call(this, config);

this.store = this.storeage();
this.store.load();

},

set: function(name, value) {
var pos, row;

if ((pos = this.store.find('name', name)) > -1) {

row = this.store.getAt(pos);
row.set('value', this.encodeValue(value));

} else {

this.store.add({
name: name,
value: this.encodeValue(value)
});

}

this.store.sync();
this.fireEvent('statechange', this, name, value);

},

get: function(name, defaultValue) {
var pos, row;

if ((pos = this.store.find('name', name)) > -1) {

row = this.store.getAt(pos);
return this.decodeValue(row.get('value'));

} else {

return defaultValue;

}

},

clear: function(name) {
var pos;

if ((pos = this.store.find('name', name)) > -1) {

this.store.removeAt(pos);
this.store.sync();
this.fireEvent('statechange', this, name, null);

}

},

storeage: Ext.emptyFn

});



To use this code you need to extend the above class in the following fashion:



/*
* File: DesktopProvider.js
* Date: 19-Jul-2011
* By : Kevin L. Esteb
*
* This module provides a state provider for the desktop using remote storage.
*
*/

Ext.define('XAS.desktop.state.DesktopProvider', {
extend: 'Ext.ux.state.RemoteStorageProvider',
uses: [
'Ext.data.Model',
'Ext.data.Store',
'Ext.ux.state.RemoteStorageProvider'
],

app_rootp: '/',

storeage: function() {

Ext.define('XAS.desktop.model.State', {
extend: 'Ext.data.Model',
idProperty: 'id',
fields: [
{ name: 'id', type: 'int' },
{ name: 'name', type: 'string' },
{ name: 'value', type: 'string' }
],
proxy: {
api: {
create: this.app_rootp + 'desktop/state/create',
read: this.app_rootp + 'desktop/state/read',
update: this.app_rootp + 'desktop/state/update',
destroy: this.app_rootp + 'desktop/state/delete'
},
type: 'ajax',
reader: {
type: 'json',
root: 'data',
totalProperty: 'count',
successProperty: 'success'
},
writer: {
type: 'json',
root: 'data',
encode: true
}
}
});

return new Ext.data.Store({
model: 'XAS.desktop.model.State'
});

}

});



And of course you start the state management like this:




Ext.state.Manager.setProvider(
Ext.create('XAS.desktop.state.DesktopProvider', {
app_roop: me.app_rootp
})
);



And of course the back end code is left as an exercise for the reader. But the backing store at least needs "id", "name" and "value" fields to work correctly.

edspencer
29 Jul 2011, 4:46 PM
Gorgeous. Do you have examples of it running?

kesteb
2 Aug 2011, 7:37 AM
Not publicly. When I figure out more of the state management stuff, I will put up a test site that can be viewed.

edspencer
2 Aug 2011, 11:18 AM
Looking forward to it

sskow200
8 Aug 2011, 9:13 AM
First off, this is an excellent solution to a common problem. Can't wait to see it get added to the library. However, I have a few caveats of doing things this way. I have an app where I have many upon many components within it. In this case, on page render, all the components fire off a mess of state events all at once (sometimes 100's). I thought setting stateful: false would fix this issue, except then I realized that components that I didn't even create were firing state events. I want full control of my StateManager and it seemed I did not have it. Digging into the code, I found that by overriding the stateful configuration and setting stateful: false at this component level solved my problem. This got rid of all the components saving state that I did not care about.



Ext.override(Ext.AbstractComponent, {
stateful: false;
});


Returning to my page, the trivial components stopped firing events! Which then brought me to the panels I had selected to have state. They still seemed to fire alot of state saving events that I was not intending them to do so. Most of the time, these events are trivial (in my case anyway). For instance, I have a portal panel that has many portlets in it. When these components render, some of them fire off multiple 'resize' events. By the time the page has been loaded, this state save has been applied multiple times which means multiple ajax requests per component (10 components x 3 requests === too many requests). In my case, my components are all set width, so having the 'resize' event as a trigger is no use for me anyway. However, looking into the code base, you can see Ext.AbstractComponent automatically sets the 'resize' event in the constructor. Here, I override Ext.AbstractComponent again to something of this effect ...




Ext.override(Ext.AbstractComponent, {
stateful: false,
//have whatever events you wish to attach state to (in my case, none)
defaultStateEvents: [],

constructor: function(config){
//omit constructor code
//removed this.addStateEvents('resize') and changed to...
this.addStateEvents(this.defaultStateEvents);
//continue omitted constructor
}
});


Voila! No more 'resize' event triggering my state manager. Drastic improvement on my requests! This brought me to my next find. On a different panel, I have a border layout with many components in it. This was firing off dozens of requests on page load as well. Digging into the 'Ext.layout.container.Border' component, I could see inside the function setupState(), again, automatically setting up state events to all child components without my knowledge! Same setup as with AbstractComponent, I overrode this component to something of this effect...



Ext.override(Ext.layout.container.Border, {

//default state events for this component layout
defaultStateEvents: [],

setupState: function(comp){
var getState = comp.getState;

comp.getState = function(){

var state = getState.call(comp) || {},
region = comp.region;

state.collapsed = !!comp.collapsed;
if (region == 'west' || region == 'east') {
state.width = comp.getWidth();
} else {
state.height = comp.getHeight();
}

return state;
};

//removed comp.addStateEvents(['collapse', 'expand', 'resize']);
comp.addStateEvents(this.defaultStateEvents);
}
});


Obviously, this is a case by case basis and not everyone will want to remove such events. However, I think what I was getting at here was a little more control of the component states. Also, if a component makes a change, why send off 1 requests per event on the same component? Seems to me that one state save for that component would be enough. Looks like there should be a queue in order here before we just start firing off a dozen events. Perhaps, this is the reason RemoteStorageProvider was not put into the original library. Even still, this same affect is happening with CookieProvider, but the results aren't as devastating because changing cookies is low-profile in terms of memory.

In either case, job well done to you for creating this class and I love working in Ext4 as a whole. Just thought I would share some of my experience with the State Management. Thanks

kesteb
8 Aug 2011, 9:57 AM
I have noticed the same behavior. From the documentation, only components that has "stateful" set "true" and a stateId should be saving there state. But in reality it seems that as soon as you provide a state provider, all components sorta save there state, and they sorta update themselves from the saved state. When you provide a "stateId' they do a better job of updating themselves. But enough on state management.

The question would be where should this "queue" be managed. Within this class or on the Store. You can always set a delayed task to fire off every so many millisecond and check to see if there is any pending changes to the store and then sync the store. I have tried this and it does work. This is similar to what saki's HttpProvider does. But it is not an elegant solution.

I would suggest that the Store should have a configuration option on how many changes should queue up before any one of the actions (create, update, delete) should be sent off to update the back end. In any case when multiple actions do happen at once the store does create an array of updates to send to the back end. For example here is the back end that handles my needs. It is written in Perl5.



package Desktop::Controller::Desktop::State;

our $VERSION = '0.01';

use Try::Tiny;
use XAS::Uaf::User;
use Desktop::Model::Database 'State';
use XAS::Class
version => $VERSION,
base => 'Desktop::Controller',
codec => 'JSON',
constants => 'TRUE FALSE ARRAY HASH',
mixin => 'Desktop::Uaf::Authenticate XAS::Lib::ExtJS',
;

use Data::Dumper;

# ----------------------------------------------------------------------
# Public Methods
# ----------------------------------------------------------------------

sub do_read {
my $self = shift;

my @data;
my @rows;
my $json;
my $count = 0;
my $schema = $self->scaffold->database->{desktop};
my $params = $self->scaffold->request->parameters;
my $uaf_user = $self->scaffold->session->get('uaf_user');
my $user = XAS::Uaf::User->new(-data => $uaf_user);
my $criteria = {
user_id => $user->attribute('user_id')
};

try {

@rows = State->search($schema, $criteria);

foreach my $row (@rows) {

$count++;

my $datum = {
id => $row->id,
name => $row->name,
value => $row->value
};

push(@data, $datum);

}

$json = $self->format_data(
-count => $count,
-data => \@data
);

} catch {

my $ex = $_;

die $ex;

};

$self->send_json($json);

}

sub do_create {
my $self = shift;

my @data;
my $json;
my $count = 0;
my $schema = $self->scaffold->database->{desktop};
my $params = decode($self->scaffold->request->parameters->get('data'));
my $uaf_user = $self->scaffold->session->get('uaf_user');
my $user = XAS::Uaf::User->new(-data => $uaf_user);

try {

if (ref($params) eq ARRAY) {

foreach my $record (@$params) {

my $datum = $self->_create_state($schema, $record, $user);
if (defined($datum)) {

push(@data, $datum);
$count++;

}

}

} else {

my $datum = $self->_create_state($schema, $params, $user);
if (defined($datum)) {

push(@data, $datum);
$count++;

}
}

$json = $self->format_data(
-count => $count,
-data => \@data
);

} catch {

my $ex = $_;

die $ex;

};

$self->send_json($json);

}

sub do_update {
my $self = shift;

my @data;
my $json;
my $count = 0;
my $schema = $self->scaffold->database->{desktop};
my $uaf_user = $self->scaffold->session->get('uaf_user');
my $user = XAS::Uaf::User->new(-data => $uaf_user);
my $params = decode($self->scaffold->request->parameters->get('data'));

try {

if (ref($params) eq ARRAY) {

foreach my $record (@$params) {

my $datum = $self->_update_state($schema, $record, $user);
if (defined($datum)) {

push(@data, $datum);
$count++;

}

}

} else {

my $datum = $self->_update_state($schema, $params, $user);
if (defined($datum)) {

push(@data, $datum);
$count++;

}

}

$json = $self->format_data(
-count => $count,
-data => \@data
);

} catch {

my $ex = $_;

die $ex;

};

$self->send_json($json);

}

sub do_delete {
my $self = shift;

my @data;
my $json;
my $count = 0;
my $schema = $self->scaffold->database->{desktop};
my $uaf_user = $self->scaffold->session->get('uaf_user');
my $user = XAS::Uaf::User->new(-data => $uaf_user);
my $params = decode($self->scaffold->request->parameters->get('data'));

try {

if (ref($params) eq ARRAY) {

foreach my $record (@$params) {

my $datum = $self->_delete_state($schema, $record, $user);
if (defined($datum)) {

push(@data, $datum);
$count++;

}

}

} else {

my $datum = $self->_delete_state($schema, $params, $user);
if (defined($datum)) {

push(@data, $datum);
$count++;

}

}

$json = $self->format_data(
-count => $count,
-data => \@data
);

} catch {

my $ex = $_;

die $ex;

};

$self->send_json($json);

}

# ----------------------------------------------------------------------
# Private Methods
# ----------------------------------------------------------------------

sub _create_state {
my ($self, $schema, $record, $user) = @_;

my $data = undef;
my $rec = {
user_id => $user->attribute('user_id'),
name => $record->{name},
value => $record->{value}
};

$schema->txn_do(sub {

my $row = State->update_or_create($schema, $rec);

$data = {
id => $row->id,
name => $row->name,
value => $row->value
};

});

return $data;

}

sub _delete_state {
my ($self, $schema, $record, $user) = @_;

my $data = undef;

if (my $row = State->find($schema, { id => $record->{id} })) {

$schema->txn_do(sub{

$row->delete;
$data = {
id => $record->{id},
name => $record->{name},
value => $record->{value}
};

});

}

return $data;

}

sub _update_state {
my ($self, $schema, $record, $user) = @_;

my $data = undef;
my $criteria = {
user_id => $user->attribute('user_id'),
name => $record->{name}
};

if (my $row = State->find($schema, $criteria)) {

$schema->txn_do(sub{

$row->value($record->{value});
$row->update;

$data = {
id => $row->id,
name => $row->name,
value => $row->value
};

});

}

return $data;

}

1;

__END__

=head1 NAME

Desktop::Controller::Desktop::State - The controller to handle the desktop state.

=head1 SYNOPSIS

=head1 DESCRIPTION

=head1 METHODS

=over 4

=item do_read

Returns the state configuration for the current user.

=item do_create

Creates a state item for the current user.

=item do_update

Updates a state item.

=item do_delete

Deletes a state item.

=back

=head1 SEE ALSO

=head1 AUTHOR

Kevin L. Esteb, E<lt>kevin@kesteb.usE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2011 by Kevin L. Esteb

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.5 or,
at your option, any later version of Perl 5 you may have available.

=cut


This module will handle a single data request or multiple data requests. It also handles the funny issue where a create on a state item that is not really a create but an update. As I said the state stuff sorta works most of the time.

sskow200
8 Aug 2011, 10:12 AM
I was able to cut back on requests by setting batchActions: true in the stores proxy. However, when I use a tabpanel with a grid in each tab, everytime a new tab is added at time of render, the state events kick in. I can't quite find the logic yet of where this is being done. I just noticed that I have 5 tabs, and 5 requests are submitted. Except each request has a chaining affect where the new tab gets appended to the post data. In reality, this is what I want except it should be 1 request (the last one), with all the tabs in one shot. Once I capture that, I believe everything will be in order.

sskow200
8 Aug 2011, 10:43 AM
Furthered, my search and found that Ext.panel.Table, the parent class to GridPanel's automatically sets the events as well. I removed these, except my issue still occurs because I need those events enabled. When the TabPanel has a new tab added, some event is triggering the grids in each Tab to fire it's events off. Trying to trace the event stack now. I also found that there is an error in the documentation referring to the 'saveBuffer' config option in Ext.state.Stateful. Documentation lists this as 'saveBuffer', however, when you look into the actual code, the config is actually referred to as 'saveDelay'. Oops!

sskow200
8 Aug 2011, 12:46 PM
Investigated my issue with 'columnresize' event firing when rendering a GridPanel. During render, when a new column is added, the 'columnresize' event is fired even if the view is not done being rendered. I prefer it to fire only when the view is rendered as if a user is resizing it in some way. As it currently exists, this does not happen.



Ext.override(Ext.grid.header.Container, {

onHeaderResize: function(header, w, suppressFocus) {
this.tempLock();

if (this.view && this.view.rendered) {
this.view.onHeaderResize(header, w, suppressFocus);
this.fireEvent('columnresize', this, header, w);
}

//was here before, commented it out for now
//this.fireEvent('columnresize', this, header, w);
}
});



Doing it this way does not seem to hurt the rendering any. I am not sure what affect may take place in the future. I don't see the harm in waiting until the view is rendered before firing this event. However, there may be good reason for this.

kesteb
26 Sep 2011, 9:33 AM
Gorgeous. Do you have examples of it running?

I do now, http://test.kesteb.us it doesn't contain on of the above fixes.

kesteb
15 Nov 2011, 10:37 AM
A minor update. I had to block in the constructor in order to make sure the store loads. Otherwise the first query is against an empty store.



/*
* File: RemoteStorageProvider.js
* Date: 20-Jul-2011
* By : Kevin L. Esteb
*
* This module provides a state provider that uses remote storage as
* the backing store.
*
* RemoteStorageProvider.js is free software: you can redistribute
* it and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* RemoteStorageProvider.js is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RemoteStorageProvider.js. If not, see
* <http://www.gnu.org/licenses/>.
*
*/

Ext.define('Ext.ux.state.RemoteStorageProvider', {
extend: 'Ext.state.Provider',
mixins: {
observable: 'Ext.util.Observable'
},
uses: [
'Ext.data.Model',
'Ext.data.Store',
'Ext.state.Provider',
'Ext.util.Observable'
],

store: {},

constructor: function(config) {

config = config || {};
this.initialConfig = config;

Ext.apply(this, config);

this.store = this.storeage();

// Have to block to load the store before leaving the constructor
// otherwise, the first query will be against an empty store.
// There must be a better way...

Ext.data.Connection.prototype.async = false;
this.store.load();
Ext.data.Connection.prototype.async = true;

this.mixins.observable.constructor.call(this, config);

},

set: function(name, value) {
var pos, row;

if ((pos = this.store.find('name', name)) > -1) {

row = this.store.getAt(pos);
row.set('value', this.encodeValue(value));

} else {

this.store.add({
name: name,
value: this.encodeValue(value)
});

}

this.store.sync();
this.fireEvent('statechange', this, name, value);

},

get: function(name, defaultValue) {
var pos, row, value;

if ((pos = this.store.find('name', name)) > -1) {

row = this.store.getAt(pos);
value = this.decodeValue(row.get('value'));

} else {

value = defaultValue;

}

return value;

},

clear: function(name) {
var pos;

if ((pos = this.store.find('name', name)) > -1) {

this.store.removeAt(pos);
this.store.sync();
this.fireEvent('statechange', this, name, null);

}

},

storeage: Ext.emptyFn

});

kesteb
2 Dec 2011, 8:32 AM
Another minor update. This one introduces the concept of throttling. It is turned off by default. Throttling will buffer state changes until a certain number have occurred, when that happens the changes are then batched to the store. You do lose some granularity in the state of the state. So you need to balance that out with performance needs.



/*
* File: RemoteStorageProvider.js
* Date: 20-Jul-2011
* By : Kevin L. Esteb
*
* This module provides a state provider that uses remote storage as
* the backing store.
*
* RemoteStorageProvider.js is free software: you can redistribute
* it and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* RemoteStorageProvider.js is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RemoteStorageProvider.js. If not, see
* <http://www.gnu.org/licenses/>.
*
*/

Ext.define('Ext.ux.state.RemoteStorageProvider', {
extend: 'Ext.state.Provider',
mixins: {
observable: 'Ext.util.Observable'
},
uses: [
'Ext.data.Model',
'Ext.data.Store',
'Ext.state.Provider',
'Ext.util.Observable'
],

store: {},
throttled: false,
queue: 10,
count: 0,

constructor: function(config) {

config = config || {};
this.initialConfig = config;

Ext.apply(this, config);

this.store = this.storeage();

// Have to block in order to load the store before leaving the
// constructor, otherwise, the first query may be against an
// empty store. There must be a better way...

Ext.data.Connection.prototype.async = false;

this.store.load();

Ext.data.Connection.prototype.async = true;

this.mixins.observable.constructor.call(this, config);

},

set: function(name, value) {
var pos, row;

if ((pos = this.store.find('name', name)) > -1) {

row = this.store.getAt(pos);
row.set('value', this.encodeValue(value));

} else {

this.store.add({
name: name,
value: this.encodeValue(value)
});

}

this.sync();
this.fireEvent('statechange', this, name, value);

},

get: function(name, defaultValue) {
var pos, row, value;

if ((pos = this.store.find('name', name)) > -1) {

row = this.store.getAt(pos);
value = this.decodeValue(row.get('value'));

} else {

value = defaultValue;

}

return value;

},

clear: function(name) {
var pos;

if ((pos = this.store.find('name', name)) > -1) {

this.store.removeAt(pos);
this.sync();
this.fireEvent('statechange', this, name, null);

}

},

sync: function() {

if (this.throttled) {

if (this.count >= this.queue) {

this.count = 0;
this.store.sync();

} else {

this.count++;

}

} else {

this.store.sync();

}

},

storeage: Ext.emptyFn

});

Vital Aaron
12 Jan 2012, 1:22 PM
The function 'storeage' should be spelled 'storage'.

ttbgwt
2 May 2012, 8:44 AM
Will this work with 4.1?

sskow200
2 May 2012, 9:55 AM
Will this work with 4.1?

It should. The code for this is fairly simple and does not use any functionality that might have been removed in 4.1.

kesteb
7 May 2012, 1:46 PM
I have been using it with a internal 4.1 application.

ttbgwt
11 Aug 2012, 8:01 AM
Any updates with this extension?

Jan (HL)
21 Aug 2012, 2:07 AM
Hey, could you release this with a dual license (GPL/Sencha) for a commercial support?

Additionally, I would prefer something like a github repo incl. the required store/model configuration. I would do this, but it's actually "your" code...

kesteb
13 Sep 2012, 7:50 AM
Updates? Sure, why not. This fixes the above noted spelling error:



/*
* File: RemoteStorageProvider.js
* Date: 20-Jul-2011
* By : Kevin L. Esteb
*
* This module provides a state provider that uses remote storage as
* the backing store.
*
* RemoteStorageProvider.js is free software: you can redistribute
* it and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* RemoteStorageProvider.js is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RemoteStorageProvider.js. If not, see
* <http://www.gnu.org/licenses/>.
*
*/

Ext.define('Ext.ux.state.RemoteStorageProvider', {
extend: 'Ext.state.Provider',
mixins: {
observable: 'Ext.util.Observable'
},
uses: [
'Ext.data.Model',
'Ext.data.Store',
'Ext.state.Provider',
'Ext.util.Observable'
],

store: {},
throttled: false,
queue: 10,
count: 0,

constructor: function(config) {

config = config || {};
this.initialConfig = config;

Ext.apply(this, config);

this.store = this.storage();

// Have to block in order to load the store before leaving the
// constructor, otherwise, the first query may be against an
// empty store. There must be a better way...

Ext.data.Connection.prototype.async = false;

this.store.load();

Ext.data.Connection.prototype.async = true;

this.mixins.observable.constructor.call(this, config);

},

set: function(name, value) {
var pos, row;

if ((pos = this.store.find('name', name)) > -1) {

row = this.store.getAt(pos);
row.set('value', this.encodeValue(value));

} else {

this.store.add({
name: name,
value: this.encodeValue(value)
});

}

this.sync();
this.fireEvent('statechange', this, name, value);

},

get: function(name, defaultValue) {
var pos, row, value;

if ((pos = this.store.find('name', name)) > -1) {

row = this.store.getAt(pos);
value = this.decodeValue(row.get('value'));

} else {

value = defaultValue;

}

return value;

},

clear: function(name) {
var pos;

if ((pos = this.store.find('name', name)) > -1) {

this.store.removeAt(pos);
this.sync();
this.fireEvent('statechange', this, name, null);

}

},

sync: function() {

if (this.throttled) {

if (this.count >= this.queue) {

this.count = 0;
this.store.sync();

} else {

this.count++;

}

} else {

this.store.sync();

}

},

storage: Ext.emptyFn

});


I use the following model:



/*
* File: WAM.desktop.model.State.js
* Date: 19-Jul-2011
* By : Kevin L. Esteb
*
* This module provides a state provider for the desktop using remote storage.
*
*/

Ext.define('WAM.desktop.model.State', {
extend: 'Ext.data.Model',
idProperty: 'id',
fields: [
{ name: 'id', type: 'int' },
{ name: 'name', type: 'string' },
{ name: 'value', type: 'string' },
{ name: 'revision', type: 'int' }
]
});


Along with this store:



/*
* File: State.js
* Date: 12-Dec-2011
* By : Kevin Esteb
*
* This is the various stores for usage within WAM.
*
*/

Ext.define('WAM.desktop.store.State', {
extend: 'Ext.data.Store',
uses: [
'Ext.data.Store',
'WAM.desktop.model.State',
'WAM.lib.mixins.MessageHandler'
],
mixins: {
messageHandler: 'WAM.lib.mixins.MessageHandler'
},

constructor: function(opts) {

var config = {
model: 'WAM.desktop.model.State',
proxy: {
timeout: 100000,
type: 'ajax',
reader: {
type: 'json',
root: 'data',
idProperty: 'id',
totalProperty: 'count',
successProperty: 'success'
},
writer: {
type: 'json',
root: 'data',
encode: true
},
listeners: {
exception: {
fn: this.onException,
scope: this
}
},
api: {
create: opts.app_rootp + 'desktop/state/create',
read: opts.app_rootp + 'desktop/state/read',
update: opts.app_rootp + 'desktop/state/update',
destroy: opts.app_rootp + 'desktop/state/delete'
}
}
};

Ext.merge(this, config);
this.callParent();

}

});


Which is specific to my application. I really don't see a need for a git repository and I don't see a need to change the license.

Jan (HL)
14 Sep 2012, 1:41 AM
Sad to here so.

Because the time goes, I'd prepared the stuff already. I had to find something for a commercial Ext JS application propose. Anywhere, I will provide a solution which works more reliable (my opinion). At GitHub and with the Sencha License being more compatible. In the next few weeks, expecting October.

Dumbledore
5 Nov 2012, 11:46 AM
Is your Solution available?

Jan (HL)
7 Nov 2012, 4:34 AM
Yes, you will find it at https://github.com/hlsolutions/extjs-components/tree/master/src/HttpStateProvider.

Dumbledore
7 Nov 2012, 9:18 AM
thanks, i will try it...

currently i use my own provider without buffering. While reading your code i found a little problem:



me.store.find('name', name), row;


should be



me.store.findExact('name', name), row;


i spent a lot of time to find a bug in my provider which overwrite a custom state id. I use following states:

ext-dashboard-portalpanel-1057
ext-dashboard


When using this this kind aof states store.find give the wrong position when searching for 'ext-dashboard'...

Bye, Dumbledore

Jan (HL)
8 Nov 2012, 9:53 AM
I will investigate this tomorrow or next week, but it seems that you have found a bug. Congrats. Issue #3 if you want to track it.

_sench_user
1 Aug 2013, 5:13 AM
Yes, you will find it at https://github.com/hlsolutions/extjs-components/tree/master/src/HttpStateProvider.
I have tried your plug-in and State Management not working with the LOCKING features for the grid.
Steps :

1. Lock any of the column in the GRID.
2. Reload the Grid.
3. ExtJs internal error on console and GRID failed to load.

Please tell me the solution for that. Thanks in advance.

Jan (HL)
1 Aug 2013, 5:17 AM
I'm currently not using the locking feature. Could you provide a small test? You can use Sencha's brand new Fiddle if you like: https://fiddle.sencha.com/#home

Tip: Because of some issues with raw data of GitHub, it is possibly required to replace a raw url like https://raw.github.com/hlsolutions/extjs-components/master/src/HttpStateProvider/hlx/base/state/HttpProvider.js to http://rawgithub.com/hlsolutions/extjs-components/master/src/HttpStateProvider/hlx/base/state/HttpProvider.js

_sench_user
1 Aug 2013, 5:37 AM
Thanks for reply.
You can test it at your own end as well. Just add "locked: true" to any of the COLUMN of the GRID. Then you can see LOCK and UNLOCK option in the column property (Just below the column hide/show).
After that you can see Lock/Unlock state in not maintained.

_sench_user
1 Aug 2013, 5:59 AM
I'm currently not using the locking feature. Could you provide a small test? You can use Sencha's brand new Fiddle if you like: https://fiddle.sencha.com/#home

Tip: Because of some issues with raw data of GitHub, it is possibly required to replace a raw url like https://raw.github.com/hlsolutions/extjs-components/master/src/HttpStateProvider/hlx/base/state/HttpProvider.js to http://rawgithub.com/hlsolutions/extjs-components/master/src/HttpStateProvider/hlx/base/state/HttpProvider.js

I have made a sample on Locking Grid by using ExtJs example.
https://fiddle.sencha.com/#fiddle/4r

Please have a look at this fiddle.

_sench_user
1 Aug 2013, 11:44 PM
It is also having issue with Ext.state.CookieProvider. Locking and Unlocking State is not persists even by using Ext.state.CookieProvider. I think it is the ExtJs issue.
If anybody has any workaround for this, then please let me know. Thanks:)

CoderDennis
9 Oct 2013, 6:57 AM
I do now, http://test.kesteb.us it doesn't contain on of the above fixes.

Have you replaced what was on your test site? It doesn't look like there's any ExtJS in use.

kesteb
9 Jan 2014, 4:53 PM
No, but the source code is here: http://svn.kesteb.us/repos/Desktop/