State provider does not restore sorters properly - extjs

I'm trying to store my grid state using the Ext.state.CookieProvider. The problem is I can't restore sorters parameters while state itself (width, order) is restoring properly.
First I've created cookieprovider in init() method of the viewport viewcontroller:
Ext.state.Manager.setProvider(Ext.create('Ext.state.CookieProvider', {}));
My store is set to auto load with remote sorting:
Ext.define('MyApp.requests.store.QueryRequestsGridStore', {
extend: 'Ext.data.Store',
model: 'MyApp.requests.model.QueryRequestsGridModel',
alias: 'store.queryRequestsGrid',
remoteSort: true,
autoLoad: true,
proxy: {
startParam: 'offset',
limitParam: 'limit',
url: '/requests',
noCache: false,
type: 'ajax',
reader: {
type: 'json',
rootProperty: 'data',
totalProperty: 'total'
}
},
});
Store is defined in grid using viewmodel binds:
bind: {
store: '{queryRequestsGrid}'
},
I'm loading the grid containing store from the viewport viewcontroller on button click like this:
var panelToAddName = Ext.create('MyApp.requests.view.QueryRequestsGridView', {});
var mainViewPort = Ext.ComponentQuery.query('#mainViewPort')[0];
var regionPanel = mainViewPort.down('[region=center][xtype=panel]');
regionPanel.removeAll();
regionPanel.add(panel);
Cookie contains sorters, but grid is loaded without any sort parameters.
"storeState":{"sorters":[{"root":"data","property":"date_completed","direction":"ASC"}]}}}
I've dug into the ext-all-debug.js source file and found initState() method of a 'Ext.state.Stateful' class.
initState: function() {
var me = this,
id = me.stateful && me.getStateId(),
hasListeners = me.hasListeners,
state, combinedState, i, len, plugins, plugin, pluginType;
if (id) {
combinedState = Ext.state.Manager.get(id);
if (combinedState) {
state = Ext.apply({}, combinedState);
if (!hasListeners.beforestaterestore || me.fireEvent('beforestaterestore', me, combinedState) !== false) {
plugins = me.getPlugins() || [];
for (i = 0 , len = plugins.length; i < len; i++) {
plugin = plugins[i];
if (plugin) {
pluginType = plugin.ptype;
if (plugin.applyState) {
plugin.applyState(state[pluginType], combinedState);
}
delete state[pluginType];
}
}
me.applyState(state);
if (hasListeners.staterestore) {
me.fireEvent('staterestore', me, combinedState);
}
}
}
}
},
If to log me.store from inside of this method, the store is shown in console as ext-empty-store while me is my loaded grid. Seems like state is applying before the store is properly loaded.
If to reuse the initState method inside beforerender grid event, sorters are restoring from cookie properly.
Any suggestions?

I have not worked with viewmodel binds as the sole bind between store and grid, and can't comment on whether that is supposed to work at all, or just by accident.
But I know that the viewmodel is processed very late, because the view has to be fully initialized first (including applyState), so the viewmodel can find all the components it wants to bind the listeners to.
So please try to add the store using any of the two "old-school" methods that work even without the viewmodel: store:'MyStoreId' or store:Ext.create('MyApp.store.MyStore') on the grid. That way, the store should be bound to the grid before applyState.
Furthermore, I see another issue you should address: Your store loads directly after store init. (autoLoad:true). At that time, it is not yet bound to the grid; thus, no sort/filter has been applied, which means that with remoteSort/remoteFilter enabled, you are sending too many requests to the server. I would recommend to load the store only after it has been applied to the grid (in grid.initComponent after the callParent call, or from grid.boxready listener). If you really want to use autoLoad, I'd recommend to look into setAutoLoad method

Related

EXTJS--treestore getCount() method returning undefined, any alternate methods to check if store has already loaded?

I have a card layout and on card "activate" event I load the store. To keep the store from loading every time that card is activated I check to see if getCount == 0 and if true, I load the store:
handleActivateGrid: function(){
if(this.myTreeGrid().getStore().getCount() == 0){
this.myTreeGrid().getStore().load();
}
I'm using this same approach elsewhere in my code and it works perfectly fine, the only difference is in this case its a Ext.data.TreeStore.
I debugged and getCount() was undefined, even AFTER the store had loaded.
Is there another approach or method I could use to implement the above?
Thanks
EDIT: just checked the docs, getCount() is not a method that exists for Ext.data.TreeStore, so that explains that
A TreeStore consists of a single root node, with child nodes.
To see if there are nodes under the root node, you can use the following
myTreeGrid().getStore().getRootNode().childNodes.length > 0
See http://docs-origin.sencha.com/extjs/4.2.2/#!/api/Ext.data.NodeInterface-property-childNodes
A more correct approach (since a load may not load any child nodes in theory) is to hookup a load event to the store, when it's initially created. From the load handler, you can set your isLoaded flag wherever it's convenient for your code.
Something like http://www.sencha.com/forum/showthread.php?91923-How-to-check-datastore-is-loaded/page2
var storeLoaded = false;
var store = Ext.create('Ext.data.TreeStore', {
proxy: {
type: 'ajax',
url: 'get-nodes.php'
},
root: {text: 'Ext JS',id: 'src', expanded: true},
listeners: {
load: function() {
storeLoaded = true;
}
}
});
var tree = Ext.create('Ext.tree.Panel', {
store: store,
renderTo: 'tree-div',
height: 300,
width: 250,
title: 'Files'
});
Just tried this code .
myTreeGrid().getStore().getRootNode().getChildAt(0) == undefined

ExtJS 4.2.1 Grid view overriden with custom XTemplate shows nothing

I am working on a switch from version 4.1.1 to 4.2.1 and finally I need to fix hopefully the last bug. We have a gridview with view being overriden by simple (?) Ext.XTemplate, as follows:
Ext.define('view.monitoring.Event',
{
extend: 'Ext.view.View',
alias: 'widget.default_monitoring_event',
itemSelector: '.monitoring-thumb-fumb',
tplWriteMode: 'overwrite',
autoWidth: true,
initComponent: function()
{
var tplPart1 = new Ext.XTemplate('<SOME HTML 1>');
var tplPart2 = new Ext.XTemplate('<SOME HTML 2>');
var tplPart3 = new Ext.XTemplate('<SOME HTML 3>');
var tplPart4 = new Ext.XTemplate('<SOME HTML 4>');
this.tpl = new Ext.Template('<BUNCH OF HTML AND TPL and TPL INSIDE TPL',
callFunc1: function(data) { /* do something with tplPart1 */ },
callFunc2: function(data) { /* do something with tplPart2 */ },
callFunc3: function(data) { /* do something with tplPart3 */ },
callFunc4: function(data) { /* do something with tplPart4 */ },
);
this.callParent(arguments;
}
}
This view is set for the grid in this way:
Ext.define('view.monitoring.WeeklyOverview',
{
extend: 'Ext.grid.Panel',
alias: 'widget.default_monitoring_weeklyoverview',
layout: 'border',
title: Lang.translate('event_monitoring_title'),
overflowX: 'hidden',
overflowY: 'auto',
initComponent: function()
{
//...
this.view = Ext.widget('default_monitoring_event', {
store: Ext.create('store.monitoring.Rows')
});
//...
}
}
Then in controller onRender the stores gets populated (the store of the grid is AJAX, loads the data and passes them to a memory store of the view). In ExtJS 4.1.1 this is working properly, data are loaded and the template is parsed and HTML is displayed containing the data.
But after the switch to 4.2.1 the HTML is no longer filled with the data and nothing is displayed but an empty HTML table (which appears as like nothing would be rendered). As there is at least some part of HTML but no data, I guess the problem might be with the data being applied for the template.
Does anybody know what might be/went wrong?
UPDATE: After debugging and the custom view template simplification I have found out, that even the custom view has set it's own store with it's own root for the data returned, it ignores that setting and simply loads the data for the store of the Ext.grid.Panel component. The AJAX response looks like:
{
user: {},
data: [
0: { ... },
1: { ... },
...
],
rows: [
0: { ... },
1: { ... },
...
]
}
Here the data should be used for Ext.grid.Panel component and the rows should be then populated by the custom view (configured with Ext.XTemplate). Seems like for the children views always the parent's store's root element is returned. Any way how to workaround this behaviour and make the custom view to use it's own store???
Finally found a solution.
The problem was that after the main grid.Panel loaded the data it distributed them also to it's (sub)view. After that even I had manually loaded the right data into this custom XTemplate, it needed to also manually override previously rendered view.
In my controller (init -> render) I needed to do this:
var me = this;
this.getMainView().store.load({
params: params || {},
callback: function(records, operation, success) {
// this line was already there, working properly for ExtJS 4.1.1, probably is now useless for ExtJS 4.2.1 with the next line
me.getCustomView().store.loadRawData(this.proxy.reader.rawData);
// I had to add this line for ExtJS 4.2.1...
me.getCustomView().tpl.overwrite(me.getCustomView().el, this.proxy.reader.rawData.rows);
}
});
This is kinda strange as I thought that with a definition within a custom view (see my question above)
tplWriteMode: 'overwrite',
it should just automatically overwrite it's view...

show dynamic data using checkbox in extjs

I have a combo box in which the user selects a value, that value is passed to the checkbox data store and it is populated dynamically from database (oracle). I tried the code below. It seems that the selected parameter is being passed to checkbox and I can see the data being populated on the console. I just can't render the checkbox on form. The error I get is: typeError: this.items[0] is undefined.
testArray = new Array();
var testStore = new Ext.data.JsonStore({
proxy:new Ext.data.HttpProxy({
method:'GET',
prettyUrls:false,
url:'kiu.htm',
listeners:{
'loadexception':{
fn:test.form.data.loadException
}
}
}),
fields:["id", "display"],
reader:new Ext.data.JsonReader({
id:'id',
root:'results',
totalProperty:'totalCount',
fields:new Ext.data.Record.create([
{name:'id',type:'int'},
{name:'display',type:'string'}
])
}),
listeners:{
load: function(t, records, options, success) {
for(var i=0; i<records.length; i++) {
testArray.push({name:records[i].data.id, boxLabel: records[i].data.display});
alert(testArray[i].id);
}
}
}
});
{
xtype:'combo',
id:'comboid3',
store:combostore,
displayField:'display',
valueField:'id',
tabIndex:1,
loadingText:'Loading combo...',
listeners :{
select:function(event){
testStore.baseParams = {
"comboid":Ext.getCmp('comboid3').getValue()
};
testStore.load();
}
}
},
{
xtype:'checkboxgroup',
fieldLabel:'Check',
items:testArray
}
Help will be appreciated!
Specifying the Ext JS version is always going to be helpful. It appears this must be 2.x or 3.x and not the current version.
The issue is timing, load calls are asynchronous so by attempting to utilize testArray in this fashion you are likely referencing an empty array by the time it parses the items property of your checkboxgroup. You have a couple options around this... one is to grab a reference to the checkbox group and add the items into it, the other is to not put the checkbox group in the form at all until the call returns and then add it with the populated items array. In either case it is likely that you will need to look up a component reference to either the FormPanel or the CheckboxGroup from within the load handler function and call the 'add' method to add child items.

Ext js Updating the total count of a paging toolbar on the fly

This should be fairly simple but I haven't found a way to do it yet.
I am using a ExtJs v.3.3.
I have a grid panel that allows record deletion with context menu.
The grid has a paging toolbar that is attached to the panel store.
The deletion process sends an ajax request to the server, on success I remove the record from the store (using the remove method).
The thing is that the paging toolbar does not reflect the change in the store , that is the total amount of records is unchanged until the store is reloaded.
Is there any way to set the total amount of records in the paging toolbar?
Thanks
This works like a charm for ExtJs ver 4.1.3.
gridStore.add(record); //Add the record to the store
gridStore.totalCount = gridStore.count(); //update the totalCount property of Store
pagingToolbar.onLoad(); //Refresh the display message on paging tool bar
Are you not able to return the totalProperty value in the response after the data has been deleted in the DB?
EDIT:
You'll need to get your response constructed properly first. This is how it should look according to the API Docs for the Paging Toolbar.
If using store's autoLoad configuration:
var myStore = new Ext.data.Store({
reader: new Ext.data.JsonReader({
totalProperty: 'results',
...
}),
...
});
var myStore = new Ext.data.Store({
autoLoad: {params:{start: 0, limit: 25}},
...
});
The packet sent back from the server would have this form:
{
"success": true,
"results": 2000,
"rows": [ // *Note: this must be an Array
{ "id": 1, "name": "Bill", "occupation": "Gardener" },
{ "id": 2, "name": "Ben", "occupation": "Horticulturalist" },
...
{ "id": 25, "name": "Sue", "occupation": "Botanist" }
]
}
This worked fine for me:
me.gridStore.add(data);
// Manually update the paging toolbar.
me.gridStore.totalCount = 500;
me.pagingToolbar.onLoad();
I had a similar problem when getting results from a third party api which had a separate url for the item count. I created a new class inheriting from the pagingtoolbar with an additional updatePager() function:
updatePager : function(){
var me = this,
pageData,
currPage,
pageCount,
afterText,
count,
isEmpty;
count = me.store.getCount();
isEmpty = count === 0;
if (!isEmpty) {
pageData = me.getPageData();
currPage = pageData.currentPage;
pageCount = pageData.pageCount;
afterText = Ext.String.format(me.afterPageText, isNaN(pageCount) ? 1 : pageCount);
} else {
currPage = 0;
pageCount = 0;
afterText = Ext.String.format(me.afterPageText, 0);
}
Ext.suspendLayouts();
me.child('#afterTextItem').setText(afterText);
me.child('#inputItem').setDisabled(isEmpty).setValue(currPage);
me.child('#first').setDisabled(currPage === 1 || isEmpty);
me.child('#prev').setDisabled(currPage === 1 || isEmpty);
me.child('#next').setDisabled(currPage === pageCount || isEmpty);
me.child('#last').setDisabled(currPage === pageCount || isEmpty);
me.child('#refresh').enable();
me.updateInfo();
Ext.resumeLayouts(true);
if (me.rendered) {
me.fireEvent('change', me, pageData);
}
}
});
I added an itemId to it when adding to the dock
dockedItems: [{
xtype: 'dynamicpagingtoolbar',
itemId: 'pager_id',
dock: 'bottom',
store: 'CompoundPharmacologyPaginatedStore',
displayInfo: true
}],
I added a setTotalCount() function to the associated store:
setTotalCount: function(count) {
this.totalCount = count;
}
Then when you want to update it call store.setTotalCount(total) and then pager.updatePager(). Remember that you will have get the pager first using something like
pager = grid_view.down('#pager_id');
"The deletion process sends an ajax request to the server, on success I remove the record from the store (using the remove method)..." - this suggest that you got method that handle "delete" action - and if you using Ext.PagingToolbar - just add one more line like this:
(this).YourPagingToolbar.doRefresh()
I put (this) in () because you did not provide any code example so I not sure how you defined it
store.totalLength = store.totalLength - 1;
this would change the number of total rows in the store, but I am not sure if this change would be reflected by the paging toolbar.
I had a similar situation when trying to use multiple paging toolbars (top/bottom of grid). The only place in the paging toolbar that updates the display gets called on store 'load'. So you can fire the event manually (but beware of unintended consequences!). In my case this worked well when run from the beforechange listener of one of my toolbars:
myStore.fireEvent('load', myStore, myStore.data.items, myStore.lastOptions);
Or ... you could override or extend the PagingToolbar to add a public method which would call or override the onLoad function
If you have multiple pages in paging toolbar, and perform insert/remove operation locally from store then use below snippet.
updatePagingToolbar: function (pagingToolbar) {
var store = pagingToolbar.getStore()
, affectedChanges = store.getCount() - store.config.pageSize;
if (pagingToolbar.store.totalCount > store.config.pageSize)
pagingToolbar.store.totalCount += affectedChanges;
else
pagingToolbar.store.totalCount = store.getCount();
pagingToolbar.onLoad();
}

ExtJS: Combobox after reload store dont set value

I think I have a very popular problem, but not found answer for it now. :)
I got 2 similar comboboxes - at first i set my value by id - comboT.setValue("22763"); and it properly set a text value linked with this id.
At second combobox i at first reload store(jsonstore) and then set value - comboC.setValue("3"); But this combo set only ID not text value (if i open list i can see what combo properly marked text value. And after (if list simply close without select) text value properly displayed at combo.
How to solve this problem?
Thanks.
Something like this, syntax may be slightly off since I am doing it from memory:
var val = 3;
var store = comboC.getStore();
store.on("load", function() {
comboC.setValue(val);
}):
store.load();
Loading the store is asynchronous, you might want to move setting the new value into the callback: event handler of store.load({...}), because otherwise, you set the value before the store is actually loaded.
EDIT: for completeness, an example, so you have an alternative version (in some cases it might be undesireable to bind the callback to the store itself, like ormuriauga did):
var val = 3;
var store = comboC.getStore();
store.load({
callback: function() {
comboC.setValue(val);
}
});
One more example on how to set the combobox's value by searching a string in the underlying data store. I was able to code this by using the samples in these answers as a baseline:
//The store's data definition must have at least a data.id field defined
set_combobox_value_from_store = function (combobox, valueField, value) {
//Get a reference to the combobox's underlying store
var store = combobox.getStore();
store.load({
callback: function () {
//Find item index in store
var index = store.find(valueField, value, false);
if (index < 0) return;
//Get model data id
var dataId = store.getAt(index).data.Id;
//Set combobox value and fire OnSelect event
combobox.setValueAndFireSelect(dataId);
}
});
In extjs 4.1 looks like combo.setValue() works when the type of valueField in the Model is "string". this was my code
Ext.define('Model.CboObras', {
extend: 'Ext.data.Model',
idProperty: 'co_obra',
fields: [{
name: 'co_obra',
type: 'int'
}, {
name: 'nb_obra',
type: 'string'
}]
});
this does not work.
When I changed my code to this:
Ext.define('Model.CboObras', {
extend: 'Ext.data.Model',
idProperty: 'co_obra',
fields: [{
name: 'co_obra',
type: 'string'
}, {
name: 'nb_obra',
type: 'string'
}]
});
After that I use this:
var store = comboC.getStore();
store.load({
callback: function() {
comboC.setValue(val);
}
});
it now works like a charm!

Resources