ExtJs bufferedrenderer plugin and locked column conflict - extjs

I need to lock some columns in grid. Here is my code with configuration of grid:
Ext.define('Crm.view.tables.baseTable', {
extend: 'Ext.grid.Panel',
alias : 'widget.baseTable',
columnLines: true,
initComponent: function(){
var that = this;
var $this = that;
this.selType = 'checkboxmodel',
this.selModel = {
allowDeselect: true,
mode: "MULTI"
};
this.plugins = [
new Ext.grid.plugin.CellEditing({
clicksToEdit: 2
}),
{
ptype: 'bufferedrenderer',
numFromEdge: 8,
trailingBufferZone: 10,
leadingBufferZone: 10
}
];
var filters = {
ftype: 'filters',
encode: true,
local: true
};
this.features = [filters];
var columns = [
new Ext.grid.RowNumberer(),
{
header : 'ID',
dataIndex: 'id',
locked : true,
filterable: true,
width : 30
},{
header : 'ФИО',
width : 100,
dataIndex: 'name',
filterable: true,
filter: {
type: 'string'
},
editor: 'textfield'
},{
header : 'Телефон',
dataIndex: 'phone',
filterable: true,
width : 75,
editor: 'textfield'
},{
header : 'Email',
dataIndex: 'email',
filterable: true,
width : 90,
editor: 'textfield'
}
];
this.columns = columns
var tbar = [{
text: 'Обновить',
handler: function(){
$this.getStore().reload();
}
},'-',{
text: 'Очистить фильтр',
handler: function () {
that.filters.clearFilters();
}
},'-']
this.tbar = tbar;
this.callParent();
}
});
But I get an error:
Uncaught TypeError: Cannot call method 'on' of undefined ext-all-debug.js:115013
When I remove bufferedrenderer plugin from config, the locked column
works. What is the problem? Thanks for answers!

This is for EXTJS 4.2.1.
Following code is init function of "Ext.data.grid.plugin.Bufferedrenderer"
When you use locking feature for grid, ExtJs wraps grid.view with "Ext.grid.locking.View".
Then init function try to find properties of grid.view which are not available through the wrapper. So I added a few line to init function (find it below with "// replace locking.View with grid.View" ) then it solves the issue.
Simply create class by extending "Ext.data.grid.plugin.Bufferedrenderer" with below init function.
Martin.P - EMH Consulting.
init: function(grid) {
var me = this,
view = grid.view,
viewListeners = {
scroll: {
fn: me.onViewScroll,
element: 'el',
scope: me
},
boxready: me.onViewResize,
resize: me.onViewResize,
refresh: me.onViewRefresh,
scope: me,
destroyable: true
};
//********************************************************************
// replace locking.View with grid.View - hayangae#gmail.com
if (view.isLockingView){
view = view.getViewForColumn();
}
//********************************************************************
// If we are using default row heights, then do not sync row heights for efficiency
if (!me.variableRowHeight && grid.ownerLockable) {
grid.ownerLockable.syncRowHeight = false;
}
// If we are going to be handling a NodeStore then it's driven by node addition and removal, *not* refreshing.
// The view overrides required above change the view's onAdd and onRemove behaviour to call onDataRefresh when necessary.
if (grid.isTree || grid.ownerLockable && grid.ownerLockable.isTree) {
view.blockRefresh = false;
view.loadMask = true;
}
if (view.positionBody) {
viewListeners.refresh = me.onViewRefresh;
}
me.grid = grid;
me.view = view;
view.bufferedRenderer = me;
view.preserveScrollOnRefresh = true;
me.bindStore(view.getStore());
view.getViewRange = function() {
return me.getViewRange();
};
me.position = 0;
me.gridListeners = grid.on('reconfigure', me.onReconfigure, me);
me.viewListeners = view.on(viewListeners);
},

Related

Form with textfield and grid: send all values to the server

In create and update forms, it is sometimes necessary to give the user the ability to dynamically add fields to values of the same type (more than one phone, more than one address, etc.).
I'm exploring several possibilities to do this.
One of them is to use a grid as a form field.
However, I have doubts about how best to implement this idea, especially on how to send all the form field values (textfield and grid) to the server (and then how to load them later in the form to edit).
Fiddles with some ideas:
One with cellediting plugin https://fiddle.sencha.com/#view/editor&fiddle/2ftp
Another one with roweditin gplugin a https://fiddle.sencha.com/#view/editor&fiddle/2fto
Not sure about the "best to implement", but I have seen so many requirements for multivalue input, that for reusability I have in my toolbox a gridfield similar to the following one:
Ext.define('Ext.ux.GridField', {
extend: 'Ext.form.FieldContainer',
alias: 'widget.gridfield',
initComponent: function () {
var me = this;
if(!me.columns) me.columns = {
dataIndex: 'field1'
};
if(!me.mapFn) me.mapFn = function(value) {
if(Ext.isObject(value)) return value;
return {
field1: value
};
};
if(!me.unmapFn) me.unmapFn = function(record) {
return record.get('field1');
};
me.grid = Ext.widget(Ext.apply({
xtype: 'grid',
viewConfig: {
markDirty: false
},
store: me.store || Ext.create('Ext.data.Store', {
fields: me.columns.map(function(column) {
return {
name: column.dataIndex,
type: column.dataType || 'auto',
defaultValue: column.defaultValue
};
}),
listeners: {
update: me.updateValue,
datachanged: me.updateValue,
scope: me
}
}),
columns: [{
xtype: 'actioncolumn',
getClass: function () {
return 'x-fa fa-times'
},
handler: function(grid, rowIndex, colIndex, item, e, record) {
grid.getStore().remove(record);
},
width: 35
}].concat(me.columns),
bbar: [{
xtype: 'button',
iconCls: 'x-fa fa-pencil',
text: 'Add',
handler: function(btn) {
var grid = btn.up('grid'),
store = grid.getStore(),
record = store.add(Ext.clone(me.emptyRecord) || {})[0];
grid.getPlugin('editor').startEditByPosition({
row: store.indexOf(record),
column: 1
});
}
}],
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
pluginId: 'editor',
clicksToEdit: 1
})
]
}, me.gridConfig)); // "gridConfig" config can override everything on each instance.
me.hiddenField = Ext.widget({
xtype: 'hiddenfield',
name: me.name,
value: '',
allowNull: false,
rawToValue: function (raw) {
return raw;
},
valueToRaw: function (value) {
return value;
},
getRawValue: function () {
return Ext.valueFrom(this.rawValue, '')
},
isEqual: function (a, b) {
return Ext.encode(a) == Ext.encode(b)
},
listeners: {
change: function(field, nV, oV) {
if(!Ext.isArray(nV)) nV = [nV];
var store = me.grid.getStore();
store.removeAll();
store.add(nV.map(me.mapFn));
}
}
});
Ext.apply(me, {
layout: 'fit',
items: [{
xtype:'container',
border: 1,
style: {
borderColor: '#d0d0d0',
borderStyle: 'solid'
},
items: [me.grid]
}, me.hiddenField]
});
me.callParent(arguments);
},
updateValue: function() {
var me = this,
grid = me.grid,
hiddenField = me.hiddenField,
nV = grid.getStore().getRange().map(me.unmapFn, me),
oV = me.hiddenField.getValue();
if(!oV || Ext.isArray(oV) && Ext.encode(nV) != Ext.encode(oV)) {
hiddenField.suspendCheckChange++;
hiddenField.setValue(nV);
hiddenField.suspendCheckChange--;
me.fireEvent('change', me, nV, oV);
}
}
});
which can then be used like this:
},{
xtype: 'gridfield',
fieldLabel: 'Contacts',
name: 'contacts',
columns: [{
text: 'Type',
dataIndex: 'type',
editor:{
xtype: 'combobox',
name: 'type',
valueField: 'name',
displayField: 'name',
store: combostore,
queryMode: 'local'
},
flex: 0.7
},{
text: 'Description',
dataIndex: 'description',
editor:{
xtype: 'textfield',
name: 'description'
},
flex: 1
}],
mapFn: function(value) {
return value;
},
unmapFn: function(record) {
return record.getData();
}
}, {
I have made a fiddle for you based on your fiddle, including working load and save operations on the form, but in ExtJS 6.x. And I have checked that it works with ExtJS 5 as well, although you have to add working icons.

How to set grid row getSelectionModel data in ext window

I am not bale to set row elected data in ext window, can you please give solution for this. my code is here..
var shiftWindow = Ext.create('Ext.window.Window', {
title: 'Edit Shift',
resizable: false,
id: 'shiftwindow',
width: 465,
//bodyPadding: 5,
modal: true,
store: shiftStorePlanner,
items: {
xtype: 'form',
id: 'idFormShift',
bodyPadding: 10,
items: shiftViewModelPlannerData
},
buttons: [{
text: 'Save',
cls: 'planner-save-button',
overCls: 'planner-save-button-over',
handler: function() {
var wi = this.up('.window')
var form = Ext.getCmp('idFormShift');
if (form.isValid()) {
shiftTimemappingarray = [];
getShiftTime();
//this.up('.window').close();
}
}
}, {
text: 'Cancel',
handler: function() {
this.up('.window').close();
}
}]
});
var host1 = Ext.getCmp('plannershifteditor');
var selection = host1._shiftPlannerGrid.getSelectionModel().getSelection();
if (selection.length === 0) {
return;
}
Ext.getCmp('shiftWindow').getForm().setValues(selection[0].data);
shiftWindow.show();
}
Depending on your ExtJS Version:
var selection = grid.getSelectionModel().getSelection();
grid.getForm().loadRecord(selection[0]);
Later you can use updateRecord() to update the model with the form values.
More Infos:
http://docs.sencha.com/extjs/6.0.2/classic/Ext.form.Basic.html#method-loadRecord
http://docs.sencha.com/extjs/6.0.2/classic/Ext.form.Basic.html#method-updateRecord
ExtJS 5 and later supports also viewmodel binding. This will do the job automatically.

Adding link against each row in grid

I need to display a 'Delete' link against each row in a editorgridpanel.
How do I create this link;as it is not mapped to any particular column in the store?
I tried the following but it does not display the link against the added records:
var sampleRecord = new Ext.data.Record.create([
{mapping: 'organizationId',name:'organizationId', type:'int'},
{mapping: 'name',name:'name', type:'string'},
{mapping: 'address',name:'address' , type:'int'}
]);
var s_grid= new Ext.grid.EditorGridPanel ({
.........
columns: [
{header: 'id', width: 120, sortable: false, dataIndex: 'organizationId'},
{header: 'name',width: 120, sortable: false, dataIndex: 'name'},
{header: 'address', sortable: false,width: 45, dataIndex: 'address'},
{header: '',width: 50, sortable: false, renderer:this.delRenderer }
],
.....
,
delRenderer:function (val, meta, rec, rowIdx) {
var tempStr="<div onclick=\"javascript:Ext.getCmp('" +"s_grid" + "').deAllocate(" + rowIdx + ");\" class='pointer'><span style='color:#0000FF'><u>Delete</u></span></div>";
return tempStr ;
},
deAllocate:function (rowIdx ) {
Ext.getCmp('s_grid').getStore().removeAt(rowIdx);
Ext.getCmp('s_grid').getView().refresh();
}
});
Help is appreciated.
You'd be better off using an ActionColumn. Anyway, you can also wrap a solution around custom element (link...) with the cellclick event. Here's an example showing both methods:
var grid = new Ext.grid.GridPanel({
renderTo: Ext.getBody()
,height: 300
,store: new Ext.data.JsonStore({
fields: ['id', 'name']
,data: [
{id: 1, name: 'Foo'}
,{id: 2, name: 'Bar'}
,{id: 3, name: 'Baz'}
]
})
,columns: [
{dataIndex: 'name'}
,{
xtype: 'actioncolumn'
,icon: 'http://images.agoramedia.com/everydayhealth/gcms/icon_delete_16px.gif'
,handler: function(grid, row) {
grid.store.removeAt(row);
}
}
,{
renderer: function(value, md, record, row, col, store) {
return '<a class="delete-link" href="#delete-record-' + record.id + '">Delete</a>';
}
}
]
,listeners: {
cellclick: function(grid, row, col, e) {
var el = e.getTarget('a.delete-link');
if (el) {
e.preventDefault();
grid.store.removeAt(row);
}
}
}
});
var lastId = 3;
setInterval(function() {
var store = grid.store,
record = new store.recordType({id: ++lastId, name: 'New record #' + lastId}, lastId);
store.add(record);
}, 3000);
Update
And just because I may be completely off topic on your question, I think your code is not working because when you call this:
renderer: this.delRenderer
Your not in a scope where this points to your grid (since it has not even been created at this point...). What you want to do is rather something like this:
var s_grid = new Ext.grid.EditorGridPanel ({
...
columns: [..., {
...
renderer: delRenderer
}]
});
function delRenderer() {
// FYI, `this` will be the column here
...
}
Or put the function inline in the grid definition...
You can change model by adding delete field :
{
name: 'delete',
convert: function () {
return 'delete';
}
You can then add extra column in your grid and check for link click by 'cellclick' event of the grid with some modifications.Here is the working example :
Working Fiddle.
Have a nice day :-)
The dataIndex config is required on your column, so just provide any field there (e.g. the id) and in your renderer function just ignore the value argument (as you are already doing).

ExtJS Grid Delete Row - Getting Selected Row

I am getting the following error when trying to get the selected row:
Uncaught TypeError: Object #<HTMLDivElement> has no method 'getView'
How do get the current selected row when I can't get the View and hence the SelectionModel?
My View Code:
Ext.define('MyLodge.view.content.MemberGrid', {
extend: 'Ext.grid.Panel',
alias: 'widget.membergrid',
initComponent: function(){
var rowEditing = Ext.create('Ext.grid.plugin.RowEditing');
var store = Ext.create('MyLodge.store.Members');
Ext.apply(this, {
height: this.height,
plugins: [rowEditing],
store: store,
stripeRows: true,
columnLines: true,
columns: [{
id :'id',
text: 'ID',
width: 40,
sortable: true,
dataIndex: 'id'
},{
text : 'Name',
flex: 1,
sortable : true,
dataIndex: 'name',
field: {
xtype: 'textfield'
}
},{
text : 'E-Mail',
width : 150,
sortable : true,
dataIndex: 'email',
field: {
xtype: 'textfield'
}
},{
text : 'Href',
width : 200,
editable: false,
sortable : true,
dataIndex: 'href'
}],
dockedItems: [{
xtype: 'toolbar',
items: [{
text: 'Add',
iconCls: 'icon-add',
handler: function(){
// empty record
store.insert(0, new MyLodge.model.Member());
rowEditing.startEdit(0, 0);
}
}, {
text: 'Delete',
iconCls: 'icon-delete',
handler: function(){
var selection = grid.getView().getSelectionModel().getSelection()[0];
if (selection) {
store.remove(selection);
}
}
},'-',{
text: 'Save',
iconCls: 'icon-save',
handler: function(){
store.sync({
success: function(response){
store.load()
}
});
}
},{
text: 'Refresh',
handler: function(){
store.load();
}
}]
}]
});
this.callParent(arguments);
}
});
One option would be adding the scope to the closure as a variable:
initComponent: function () {
var me = this;
.
.
So in your handler you can use me to refer to the grid:
{
text: 'Delete',
iconCls: 'icon - delete ',
handler: function () {
var selection = me.getView().getSelectionModel().getSelection()[0];
if (selection) {
store.remove(selection);
}
}
}
Working example: http://jsfiddle.net/Sn4fC/
A second option can be setting a click listener instead of the handler and specifying the scope:
{
text: 'Delete',
iconCls: 'icon - delete ',
listeners: {
click: {
scope: this,
fn: function () {
var selection = this.getView().getSelectionModel().getSelection()[0];
if (selection) {
store.remove(selection);
}
}
}
}
}
Working example: http://jsfiddle.net/XyBbF/
The issue is that 'grid' is not available in the handler function scope or it is not the one what you actually expected it to be.
handler: function(){
//.... keep the debug point here in browser developer tool and verify the value of grid in console. It is definitely not an instance of the grid u expected hence it wont have getView() method.
var selection = grid.getView().getSelectionModel().getSelection()[0];
}
Try getting the reference of grid and use it in handler as shown below:
Ext.define('MyLodge.view.content.MemberGrid', {
extend: 'Ext.grid.Panel',
alias: 'widget.membergrid',
id: 'membergrid',
......
handler: function(){
var grid = Ext.getCmp('membergrid'); // where 'membergrid' is the id defined in grid config
var selection = grid.getView()......

ExtJS Filters Feature Error

I am a novice in ExtJS and I am trying to include filters in ExTJS grid but I am getting an error like failed loading file "feature.filters". Below is my function for creating Grid and I am invoking this function from another HTML page.
function ExtJSGrid(tableId,headerInfo,data){
Ext.Loader.setConfig({enabled: true});
Ext.Loader.setPath('Ext.ux', 'http://vmxplambardi:19086/teamworks/script/extjs/examples/ux');
Ext.require([
'Ext.grid.*',
'Ext.data.*',
'Ext.ux.grid.FiltersFeature',
'Ext.toolbar.Paging'
]);
var tableId=tableId+"-div";
var fields=[],columns=[],dataIndex='cell';
var filters = {
ftype: 'filters',
local:true,
filters: [{
type: 'string',
dataIndex: 'cell1'
}, {
type: 'string',
dataIndex: 'cell2'
}, {
type: 'string',
dataIndex: 'cell3'
}]
};
document.getElementById(tableId).innerHTML='';
for(var i=1;i<=headerInfo.length;i++)
{
var cellObj={},columnObj={};
cellObj.name=dataIndex+i;
fields.push(cellObj);
columnObj.text=headerInfo[i-1];
columnObj.dataIndex=dataIndex+i;
columns.push(columnObj);
}
var store = Ext.create('Ext.data.ArrayStore', {
fields:fields,
data: data
});
var grid = Ext.create('Ext.grid.Panel', {
store: store,
columns:columns,
width:'100%',
forceFit:true,
features: [filters],
renderTo: tableId
});
}
Please let me know if I am missing anything?
Javascript has no block level scope, so variables defined in the for loop are actually defined at the top of the function (hoisted) and survive the loops. So you are always overwrite the vars.
I've got it working like this:
var filters = {
ftype: 'filters',
// encode and local configuration options defined previously for easier reuse
encode: false, // json encode the filter query
local: true, // defaults to false (remote filtering)
// Filters are most naturally placed in the column definition, but can also be
// added here.
filters: [
{
type: 'boolean',
dataIndex: 'visible' //Just an example
}
]
};
var myFilterGrid = new Ext.create('Ext.ux.LiveSearchGridPanel', {
title: 'someTitle',
selType: 'cellmodel',
store: myStore,
columns:[
{
header: "Column1",
width: 90,
sortable: true,
dataIndex: 'INDEX1',
filterable: true, //<---
filter:{ //<---
type:'string'
}
},
....
{
header: "Another Column",
width: 85,
sortable: true,
dataIndex: 'INDEX2'
}],
features: [filters] //<---
});
And be sure to include
'Ext.ux.grid.FiltersFeature'
Hope this helps!

Resources