I am dealing with property grid. I want to prevent auto sorting of column names for property grid.
here is my code. Bold highlighted code is my source for property grid and its order is just like I want to see. But Ext is auto sorting column orders alphabeticly. How can I prevent that.
Thanks for any suggestion.
Ext.ns('Application.propertygrid');
Application.propertygrid.FileDetail = Ext.extend(Ext.grid.PropertyGrid, {
title: 'File Detail',
height: 200,
border: false,
stripeRows: true,
flex: 1,
initComponent: function () {
Application.propertygrid.FileDetail.superclass.initComponent.apply(this, arguments);
},
source: {
Name: 'Please select a file',
Type: 'Please select a file',
Size: 'Please select a file',
Path: 'Please select a file',
FullPath: 'Please select a file',
Width: 'Please select a file',
Height: 'Please select a file'
},
listeners: {
beforeedit: function(){
return false; // prevent editing
},
headerclick: function(){
return false; // prevent column sorting on click
}
}
})
Ext.reg('filedetail', Application.propertygrid.FileDetail);
Yeah. I've done with it. And here is the solution.
var p = new Ext.grid.PropertyGrid({
...
// do not specify 'source' here
});
delete p.getStore().sortInfo; // Remove default sorting
p.getColumnModel().getColumnById('name').sortable = false; // set sorting of first column to false
p.setSource(source); // Now load data
This will not work for the Extjs 4:
delete p.getStore().sortInfo; // Remove default sorting
p.getColumnModel().getColumnById('name').sortable = false; // set sorting of first column to false
p.setSource(source); // Now load data
You can try this:
p.getStore().sorters.items = [] // which should remove sorting information from the store
p.setSource(source) // now load the data
For Extjs 3.4 should only need:
delete propertygrid.getStore().sortInfo;
This is the way I do this:
When I define my columns I set the sortable property to false and I define my own 'sortable flag', like this:
var column = {
xtype: 'column-component',
...
sortable: false,
sortableColumn: true
}
Later when a user clicks on the column header (the headerclick event gets triggered) and I check if the column is sortable or not, like this:
onHeaderClick: function(ct, column, e) {
if (column.sortableColumn) {
// do your own sorting ...
}
}
Related
I use ExtJs 6.6.0 Classic. The grid component supports multi-column sorting (I use remoteSort: true, remoteFilter: true). Whenever the user clicks on a column header, that column becomes the first column in the order by list. But I cannot find how an end user is supposed to clear the sorting for a column. The context menu available through the column header doesn't have a "Clear Sort" option.
See also this kitchensink example.
I feel like I am missing something. There is a sortClearText config for the column inherited from the header, but I could not find a place where it's used (I thought that perhaps there is some config I can use to add the Clear Sort menu item to the column context menu).
I could add a button to execute the action of clearing the sorting of the store, as a last resort, but I don't like it.
Is there a simple way to add a Clear Sort option for a grid column through the Extjs components configuration?
Thank you
I also did not find, but you can use the following override:
Ext.define('overrides.grid.header.Container', {
override: 'Ext.grid.header.Container',
getMenuItems: function() {
var me = this,
menuItems = [],
hideableColumns = me.enableColumnHide ? me.getColumnMenu(me) : null;
if (me.sortable) {
menuItems = [{
itemId: 'ascItem',
text: me.sortAscText,
iconCls: me.menuSortAscCls,
handler: me.onSortAscClick,
scope: me
}, {
itemId: 'descItem',
text: me.sortDescText,
iconCls: me.menuSortDescCls,
handler: me.onSortDescClick,
scope: me
}, {
itemId: 'dropSortItem',
text: me.sortClearText,
//iconCls: me.menuSortDescCls, // Your icon
handler: me.onSortClearClick,
scope: me
}];
}
if (hideableColumns && hideableColumns.length) {
if (me.sortable) {
menuItems.push({
itemId: 'columnItemSeparator',
xtype: 'menuseparator'
});
}
menuItems.push({
itemId: 'columnItem',
text: me.columnsText,
iconCls: me.menuColsIcon,
menu: hideableColumns,
hideOnClick: false
});
}
return menuItems;
},
onSortClearClick: function() {
var menu = this.getMenu(),
activeHeader = menu.activeHeader,
store = this.up('grid').getStore();
store.getSorters().each(function(sorter) {
if(sorter.initialConfig.property == activeHeader.dataIndex) {
store.getSorters().remove(sorter)
}
}, this);
}
});
I have in my ExtJS 4.2.1 Application a grid with the following editable column:
text: 'Location',
dataIndex: 'LocationId',
width: 140,
renderer: function(value) {
var record = me.store.findRecord('LocationId', value);
return record.get('Description');
},
editor: {
xtype: 'combobox',
typeAhead: true,
triggerAction: 'all',
store: Ext.create('App.store.catalog.Location', {
autoLoad: true
}),
displayField: 'Description',
valueField: 'LocationId',
listConfig: {
width: 250,
loadingText: 'Searching...',
// Custom rendering template for each item
getInnerTpl: function() {
return '<b>{Code}</b><br/>(<span style="font-size:0.8em;">{Description}</span>)';
}
}
}
The combo has a renderer to display the Description of the LocationId selected.
Then, my grid has the feature 'Ext.grid.plugin.CellEditing' so I can edit just that column cell.
The problem that I have is when I press the "Update" button, the combo display value returns to the original it used to have, even if the LocationId in the record has the right value.
This is my code that gets fired when the user press the "Update" button.
me.rowEditing = Ext.create('Ext.grid.plugin.RowEditing', {
clicksToMoveEditor: 1,
autoCancel: false,
listeners: {
edit: function(editor, e) {
var record = e.record;
me.setLoading('Updating...');
App.util.Ajax.request({
noMask: true,
url: '/api/catalog/UpdateEmployeeLocation',
jsonData: record.getData(),
success: function(response, opts) {
var obj = Ext.decode(response.responseText);
if (obj.success) {
// commit changes (no save just clear the dirty icon)
me.getStore().commitChanges();
}
},
callback: function() {
me.setLoading(false);
}
});
}
}
});
The record is saved correctly in my database but the combo display value is not updated with the description that corresponds to the LocationId. If I reload the store from server again then It shows correctly.
So, there is something wrong with the renderer in my column that is not updating the value after I update my record.
Any clue on how to get around this?
Thanks.
You are setting dataIndex as 'LocationId' but no where you are changing the 'LocationId', you are just changing description and updating it in rendered method. Since there no change in 'LocationId', store doesn't consider it as dirty field and hence rendered function is not getting called. One quick and dirty way could be instead of using 'LocationId', create another field in the model say 'LocationIdchangeTraker'. Use 'LocationIdchangeTraker' instead of 'LocationId' in data index. It doesn't not effect your view because you are changing the value in reneerer function. Now whenever you update the function change the value of 'LocationIdchangeTraker' as shown below.
record.set('LocationIdchangeTraker',Ext.getId());
I have a working sort-able grid using the ext 3.4 grid filter plugin. I would like to default the active column to filter true values. User who needs the inactive records could remove the filter. How do I specify a default filter column and value?
Thanks in advance!
colModel: new Ext.grid.ColumnModel({
defaults: {
sortable: true
// How do I specify a default filter value
//
// Only show active records unless the user changes the filter...
},
columns: [{
dataIndex:'f_uid',
id:'f_uid',
header:'ID',
hidden:true
}, {
dataIndex:'f_name',
id:'f_name',
header:'Name',
}, {
xtype:'booleancolumn',
dataIndex:'f_active',
id:'f_active',
header:'Active',
filterable:true,
trueText:'Active',
falseText:'Inactive'
}]
I realise this is an old question but it took me a while to find a solution, therefore I thought I would share.
1) The filter can be set using the value property in the filter.
filter: {
type: 'LIST',
value: ['VALUE TO FILTER']
}
2) In order to initially filter the data use the filterBy() method in the store. This could be defined in the onRender event handler.
this.getStore().load({
scope:this,
callback: function() {
// filter the store
this.getStore().filterBy(function(record, id) {
// true will display the record, false will not
return record.data.DATA_TO_FILTER == 'VALUE TO FILTER ';
});
}
});
The answer was in the Filter.js source code. The filter object within the column definition can be used to configure the default behavior.
}, {
xtype:'booleancolumn',
dataIndex:'f_active',
id:'f_active',
header:'Active',
trueText:'Active',
falseText:'Inactive',
filterable:true,
filter: {
value:1, // 0 is false, 1 is true
active:true // turn on the filter
}
}
I have encountered the same problem and I found that #John's answer is right, I can make it work with the sample http://dev.sencha.com/deploy/ext-4.0.0/examples/grid-filtering/grid-filter-local.html, for the grid-filter-local.js, just add the code like:
grid.getStore().load({
scope:this,
callback: function() {
// filter the store
grid.getStore().filterBy(function(record, id) {
// true will display the record, false will not
return record.data.size === 'small';
});
}
});
before the original code store.load(), and wipe off the store.load().
Then it will only show the record with size equals 'small' at the first load of the web page. Cheers!
I've made a universal helper class that allows you to set any default values in column definition.
https://gist.github.com/Eccenux/ea7332159d5c54823ad7
This should work with both remote and static stores. Note that this also works with filterbar plugin.
So your column item is something like:
{
header: 'Filename',
dataIndex: 'fileName',
filter: {
type: 'string',
// filename that starts with current year
value: Ext.Date.format(new Date(), 'Y'),
active:true
}
},
And then in your window component you just add something like:
initComponent: function() {
this.callParent();
// apply default filters from grid to store
var grid = this.down('grid');
var defaultFilters = Ext.create('Ext.ux.grid.DefaultFilters');
defaultFilters.apply(grid);
},
Using Ext.ux.grid.FiltersFeature, I have remote filters and I am trying to write a function to apply a date filter on a grid column programmatically (rather than clicking on the filter drop down menu in the column header). The first time I run the function the grid store gets reloaded without the filter. When I run the function a second time (and every time thereafter) it works totally fine, the store reloads with the filters. Here is the gist of the function I have:
// a filter object for testing
aFilter = {type: 'date', field: 'a_date_field', comparison: 'gt', value: '2012-03-08 00:00:00'}
var grid = Ext.create('Ext.grid.Panel', {
store: store,
features: [{
ftype: 'filters',
}],
columns[{
header: 'ID',
dataIndex: 'id',
itemId: 'id',
width: 40,
}, {
xtype: 'datecolumn',
header: 'Date',
dataIndex: 'a_date_field',
itemId: 'a_date_field',
width: 75,
format:'j-M-Y',
filterable: true
}],
listeners: {
'afterrender': function() {
// Need to create the filters as soon as the grid renders
// rather than waiting for the user to click on the header
grid.filters.createFilters();
}
},
bbar: [{
text: 'Do a filter',
handler: function() {
// get the filter that is attached to the grid
var gridFilter = grid.filters.getFilter(aFilter.field);
// have to do this to create a menu for this filter
gridFilter.init({dataIndex: aFilter.field, type: aFilter.type, active: true});
// if this column is a date filter column
if (gridFilter.type == 'date') {
var dateValue = Ext.Date.parse(aFilter.value, 'Y-m-d H:i:s');
if (filter.comparison == 'gt') {
gridFilter.setValue({after: dateValue});
} else {
gridFilter.setValue({before: dateValue});
}
}
}
}
});
I also found that this function works the first time if I click on any grid header menu before I run the function.
I've been trying to find out what changes are made to the grid which make the filter work after the first attempt fails or what clicking on any grid header does to make it work. But nothing I add seems to fix it so it will run the first time. Has anyone implemented this successfully?
I have workaround:
bbar: [{
text: 'Do a filter',
handler: function() {
var grid = this.up('grid');
var dateValue = Ext.Date.parse(aFilter.value, 'Y-m-d H:i:s');
var value = aFilter.comparison == 'gt' ? {after: dateValue} : {before: dateValue};
var gridFilter = grid.filters.getFilter(aFilter.field);
if (!gridFilter) {
gridFilter = grid.filters.addFilter({
active: true,
type: aFilter.type,
dataIndex: aFilter.dataIndex,
});
gridFilter.menu.show();
gridFilter.setValue(value);
gridFilter.menu.hide();
} else {
gridFilter.setActive(true);
}
Ext.Function.defer(function(){
gridFilter = grid.filters.getFilter(aFilter.field);
gridFilter.setValue(value);
}, 10);
}
}]
As you can see I actually apply filter 2 times.
As an update, I expanded this function and modified it to work with ExtJS 4.1.1
Here is an example of the function to set grid filters dynamically (without the user needing to click on the menu items). Afterwards, the filtered items will be visible to the user in the grid column header menus as if he clicked on them and set them manually.
The "grid" argument is a grid with FiltersFeature that you want to filter. The other argument is an array of "filter" objects (I'll show an example below), the function simply applies all the passed "filter" objects to the grid.
doGridFilter: function(grid, filters) {
// for each filter object in the array
Ext.each(filters, function(filter) {
var gridFilter = grid.filters.getFilter(filter.field);
gridFilter.setActive(true);
switch(filter.data.type) {
case 'date':
var dateValue = Ext.Date.parse(filter.data.value, 'm/d/Y'),
value;
switch (filter.data.comparison) {
case 'gt' :
value = {after: dateValue};
break;
case 'lt' :
value = {before: dateValue};
break;
case 'eq' :
value = {on: dateValue};
break;
}
gridFilter = log.filters.getFilter(filter.field);
gridFilter.setValue(value);
gridFilter.setActive(true);
break;
case 'numeric':
var value;
switch (filter.data.comparison) {
case 'gt' :
value = {gt: filter.data.value};
break;
case 'lt' :
value = {lt: filter.data.value};
break;
case 'eq' :
value = {eq: filter.data.value};
break;
}
gridFilter = log.filters.getFilter(filter.field);
gridFilter.setValue(value);
gridFilter.setActive(true);
break;
case 'list':
gridFilter = log.filters.getFilter(filter.field);
gridFilter.menu.setSelected(gridFilter.menu.selected, false);
gridFilter.menu.setSelected(filter.data.value.split(','), true);
break;
default :
gridFilter = log.filters.getFilter(filter.field);
gridFilter.setValue(filter.data.value);
break;
}
});
}
Here's an example of a "filter" object array.
// an example of a "filters" argument
[{
field: 'some_list_column_data_index',
data: {
type: 'list',
value: 'item1,item2,item3,item4,item5,item6,item7'
}
}, {
field: 'some_date_column_data_index',
data: {
type: 'date',
comparison: 'gt',
value: '07/07/2007'
}
}]
One caveat, you need to "create" the filters manually before using this function. Normally FiltersFeature grid filters are "created" the first time a user clicks on one of them, that may not happen if the user just wants to apply one of these predefined filters.
That can be handled easily by including this afterrender listener in the gridpanel.
listeners: {
// must create the filters after grid is rendered
afterrender: function(grid) {
grid.filters.createFilters();
}
}
Just add
filter: true
to grid columns description like this:
me.columns = [
{header:"Name", dataIndex:"name", editor:"textfield", filter: true},
];
if you want to get the filter work after the first attempt, first instance create.
Here is something that may be worth looking into. It seems that the filters plugin is listening for menucreate event to initialize the filters. I wonder if menu create event is deferred until necessary and hence the filters don't get initialized?
/**
* #private Handle creation of the grid's header menu. Initializes the filters and listens
* for the menu being shown.
*/
onMenuCreate: function(headerCt, menu) {
var me = this;
me.createFilters(); //<------
menu.on('beforeshow', me.onMenuBeforeShow, me);
},
Do you want to apply grid filter or may be store.filter() capability would suit you better? In this case just filter the store, and grid will display filtered records.
I discovered another way to implement this. It appears that grid features are only bound to the grid after the grid is rendered. This means that any setup of the filter will not take effect until after the grid is rendered. The initial load of the store appears to be initiated before the grid is rendered.
I solved my problem by creating my store with a memory proxy containing no data.
me.store = Ext.create('Ext.data.Store', {
model: 'SummaryData',
data: [],
proxy: {
type: 'memory',
reader: 'array'
},
remoteSort: true,
remoteFilter: true
});
Then set up an afterrender handler on the grid to poke in the correct proxy and initiate a load of the store.
afterrender: function () {
var me = this;
me.store.setProxy({
type: 'ajax',
url : '/print_unallocated/change_site__data',
reader: {
type: 'json',
root: 'rows'
},
listeners: {
exception: function (proxy, response) {
Max.reportException(response);
}
}
});
me.filters.createFilters();
me.store.load();
},
In the source, you can see a comment related to this.
// Call getMenu() to ensure the menu is created, and so, also are the filters. We cannot call
// createFilters() withouth having a menu because it will cause in a recursion to applyState()
// that ends up to clear all the filter values. This is likely to happen when we reorder a column
// and then add a new filter before the menu is recreated.
me.view.headerCt.getMenu();
You can test whether the menu has been created before applying your filter. If it hasn't, do it yourself.
if(!grid.getView().headerCt.menu){
grid.getView().headerCt.getMenu();
}
I am using EXTJS with an editorgridpanel and I am trying to to insert a combobox, populated with JsonStore.
Here is a snapshot of my code:
THE STORE:
kmxgz.ordercmpappro.prototype.getCmpapproStore = function(my_url) {
var myStore = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: my_url
, method: 'POST'
})
, reader: new Ext.data.JsonReader({
root: 'rows',
totalProperty: 'total',
id: 'list_cmpappro_id',
fields: [
{name: 'list_cmpappro_id', mapping: 'list_cmpappro_id'}
, {name: 'list_cmpappro_name', mapping: 'list_cmpappro_name'}
]
})
, autoLoad: true
, id: 'cmpapproStore'
, listeners: {
load: function(store, records, options){
//store is loaded, now you can work with it's records, etc.
console.info('store load, arguments:', arguments);
console.info('Store count = ', store.getCount());
}
}
});
return myStore;
};
THE COMBO:
kmxgz.ordercmpappro.prototype.getCmpapproCombo = function(my_store) {
var myCombo = new Ext.form.ComboBox({
typeAhead: true,
lazyRender:false,
forceSelection: true,
allowBlank: true,
editable: true,
selectOnFocus: true,
id: 'cmpapproCombo',
triggerAction: 'all',
fieldLabel: 'CMP Appro',
valueField: 'list_cmpappro_id',
displayField: 'list_cmpappro_name',
hiddenName: 'cmpappro_id',
valueNotFoundText: 'Value not found.',
mode: 'local',
store: my_store,
emptyText: 'Select a CMP Appro',
loadingText: 'Veuillez patienter ...',
listeners: {
// 'change' will be fired when the value has changed and the user exits the ComboBox via tab, click, etc.
// The 'newValue' and 'oldValue' params will be from the field specified in the 'valueField' config above.
change: function(combo, newValue, oldValue){
console.log("Old Value: " + oldValue);
console.log("New Value: " + newValue);
},
// 'select' will be fired as soon as an item in the ComboBox is selected with mouse, keyboard.
select: function(combo, record, index){
console.log(record.data.name);
console.log(index);
}
}
});
return myCombo;
};
The combobox is inserted in an editorgridpanel.
There's a renderer like this:
Ext.util.Format.comboRenderer = function(combo){
return function(value, metadata, record){
alert(combo.store.getCount()); //<== always 0!!
var record = combo.findRecord(combo.valueField || combo.displayField, value);
return record ? record.get(combo.displayField) : combo.valueNotFoundText;
}
};
When the grid is displayed the first time, instead of have the displayField, I have : "Value not found."
And I have the alert : 0 (alert(combo.store.getCount())) from the renderer.
But I can see in the console that the data have been correctly loaded!
Even if I try to reload the store from the renderer (combo.store.load();), I still have the alert (0)!
But when I select the combo to change the value, I can see the data and when I change the value, I can see the displayFiel!
I don't understand what's the problem?
Since now several days, I already tried all the solutions I found...but still nothing!
Any advice is welcome!
Yoong
The core of the problem is that you need to hook an listener which will execute after the grid store loaded. That listener will then convert the combo grid content to the displayField content instead of valueField.
Here is my resolution to this issue.
Ext.ns("Ext.ux.renderer");
Ext.ux.renderer.ComboBoxRenderer = function(combo, grid) {
var getValue = function(value) {
var record = combo.findRecord(combo.valueField, value);
return record ? record.get(combo.displayField) : value;
};
return function(value) {
var s = combo.store;
if (s.getCount() == 0 && grid) {
s.on({
load: {
single: true,
fn: function() {
grid.getView().refresh()
}
}
});
return value
}
return getValue(value)
}
};
You can use this renderer in your code like the following:
{
header: 'Header',
dataIndex: 'HeaderName',
autoWidth: true,
renderer: Ext.ux.renderer.ComboBoxRenderer(combo, grid),
editor: combo
}
This is a common issue. If you need a store value to display in the combo, handle the store's load event and select the appropriate value in the combo only after that. You shouldn't need a specific record just to render the combo up front.
I would recommend adding the field required from the combobox's store, to the grid's store, and change the renderer to use that field. (It does not have to be in the database)
and on the afteredit event of the grid, grab that value from the combobox's store and copy it to the grid's store.
This would yield better performance.
Actually, the problem seems to be that the grid renders the combobox values BEFORE the loading of the store data.
I found something here.
But when I applied the override, still the same problem...
The question i: how to defer the render of the combobox, waiting for the loading of the store?