Stop double click action on a action-column - extjs4

I've a grid panel in extJS 4.1.1. With other columns I've an actioncolumn(xtype:'actioncolumn'). I include a handler with that column. When I click it , it works fine and open a new window with many things loading. But I got error when I double click on that column. Advance welcome for any help.......
{
text : 'Signature',
menuDisabled : true,
sortable : false,
id : 'signature',
xtype : 'actioncolumn',
width : 60,
items : [{
icon : "${resource(dir: 'images', file: 'ADD01003.png')}",
tooltip : 'Add Signature',
scope : this,
handler : function(grid, rowIndex, colIndex) {
var records = grid.getStore().getAt(rowIndex).data,
fullName = records.fullName,
nickName = records.nickName,
salutation = grid.getStore().getAt(rowIndex).raw.m00i012001.name,
searchValue = records.id ;
var filters = new Array();
var store =Ext.data.StoreManager.lookup('S02X004001');
store.clearFilter();
filters.push({property:'member', value:searchValue});
store.loadPage(1, {
filters : filters,
callback : function(records, options, success) {
var view = Ext.widget('v02x004001');
view.show();
Ext.getCmp('fullName-sv02x00400104').setValue(fullName);
Ext.getCmp('nickName-sv02x00400104').setValue(nickName);
Ext.getCmp('member-sv02x00400104').setValue(searchValue);
Ext.getCmp('salutation-sv02x00400104').setValue(salutation);
}
});
}
}]
}

Not to resurrect any Zombies, but for anyone having the same Problem in ExtJS 4, 5 or 6 this workaround should work (maybe the Classnames need to be slightly modified, pasted Code is proven to work in 5.1.1).
The trick is to not modify the actioncolumn but to fix the itemdblclick event handler one might have like so:
listeners:
{
itemdblclick: function(v, record, item, index, e)
{
var flyTarget = Ext.fly(e.target);
if(flyTarget.hasCls('x-action-col-icon') || flyTarget.hasCls('x-grid-cell-inner-action-col'))
{
e.stopEvent();
return;
}
//your code here
}
}

Related

Extjs 4.1.3 Nested Grid using rowexpander plugin not working

Im trying to use the example from sencha and configure rowexpander. When the grid renders the expander is already opened for each row and with nothing inside. When i click on its icon the following error is generated:
Uncaught TypeError: Cannot call method 'addCls' of null
Have anyone else faced this issue ? Can someone post an example of hpw to use rowexpander please.
plugins : [{
ptype : 'rowexpander',
rowBodyTpl : ['<div id="NestedGridRow-{rowId}"></div>'],
pluginId : 'rowexpanderplugin',
selectRowOnExpand : true,
// this gives each row a unique identifier based on record's "acct_no"
rowBodyTpl : ['<div id="NestedGrid-{name}" ></div>'],
// stick a grid into the rowexpander div whenever it is toggled open
toggleRow : function(rowIdx) {
var rowNode = this.view.getNode(rowIdx), row = Ext.get(rowNode), nextBd = Ext.get(row).down(this.rowBodyTrSelector), hiddenCls = this.rowBodyHiddenCls, record = this.view.getRecord(rowNode), grid = this.getCmp(), name= record.get('name'), targetId = 'NestedGrid-' + name;
if (row.hasCls(this.rowCollapsedCls)) {
row.removeCls(this.rowCollapsedCls);
this.recordsExpanded[record.internalId] = true;
this.view.fireEvent('expandbody', rowNode, record, nextBd.dom);
if (rowNode.grid) {
nextBd.removeCls(hiddenCls);
rowNode.grid.doComponentLayout();
rowNode.grid.view.refresh();
} else {
// this is the store for the inner grid
Ext.create('innsergridstore', {
autoLoad : {
callback : function() {
// create the inner grid and render it to the row
nextBd.removeCls(hiddenCls);
var grid = Ext.create('NestedGrid', {// <-- this is my "inner" grid view
renderTo : targetId,
store : this,
row : row
});
rowNode.grid = grid;
grid.suspendEvents();
}
}
});
}
} else {
row.addCls(this.rowCollapsedCls);
nextBd.addCls(this.rowBodyHiddenCls);
this.recordsExpanded[record.internalId] = false;
this.view.fireEvent('collapsebody', rowNode, record, nextBd.dom);
}
}
}]

One view and multiple controller actions for the same button in EXTJS

Am having a delete button in my EXTJS Application. On clicking the button, am opening a confirmation form, asking the user are they sure to delete the item. The delete button is a part of many forms in my Application. And irrespective of the form being used, am opening the confirmation window.
And on clicking the yes button in the confirmation window, i want to do some action. But these actions have to be specific to the form that was opened first.So, am confused about how to use the same view, the same button, but different actions depending upon the first form that was opened.
View: This is the window that opens on clicking the delete button in any of the forms
Ext.define('app.view.GenMessageWin', {
extend : 'Ext.panel.Panel',
alias : 'widget.genmessagewin',
var fp = {
xtype : 'panel',
itemId : 'MSGPANEL',
width : Width,
height : 150,
cls : 'msg effect1',
layout : 'form',
border : false,
items : [{
xtype : 'panel',
//cls : 'winTitle',
html : msgTxt,
border : 0
}, {
xtype : 'form',
itemId : 'MSGFORM',
border : false,
title : '',
buttonAlign : 'center',
fieldDefaults : {
msgTarget : 'side',
labelWidth : 110,
size : 30
},
buttons : [{
text : LANG.BTYES,
iconCls : 'icon-tick-tb',
iconAlign : 'right',
cls : 'tip-btn',
action : 'delete',
id : 'BTYES'
}, {
text : LANG.BTNO,
iconCls : 'icon-cross-tb',
iconAlign : 'right',
cls : 'tip-btn',
action : 'notDelete',
id : 'BTNO'
} ]
Controller
init : function() {
this.control({
'button[action = delete]' : {
click : this.delete
},
'button[action = notDelete]' : {
click : this.notDelete
},
So, in the delete action, we have to determine which form has been opened in the first place, and then delete the data accordingly.
You have 3 options:
1) Make the selector more specific:
'form1 button[action=delete]': {
click: this.form1Delete
},
form1Delete: function(){
this.showMsg(function() {
// form 1 delete
});
}
2) Traverse back up the component hierarchy and find the open form
onDelete: function(btn) {
var form = btn.up('form'); // find an xtype form or subclass
if (form.someCondition) {
//foo
} else {
//bar
}
}
3) As suggested by Dmitry. You'll need to convert it over to 'MVC style'.
Ext.define('ConfirmButton', {
extend: 'Ext.button.Button',
title: '',
msg: '',
requires: ['Ext.window.MessageBox'],
initComponent: function(){
this.callParent();
this.on('click', this.handleClick, this);
},
handleClick: function(){
Ext.MessageBox.confirm(this.title, this.msg, this.checkResponse, this);
},
checkResponse: function(btn){
if (btn == 'yes') {
this.fireEvent('confirm', this);
}
}
});
Ext.onReady(function(){
var btn = new ConfirmButton({
renderTo: document.body,
text: 'Foo',
title: 'Should I',
msg: 'Are you sure'
});
btn.on('confirm', function(){
console.log('Do something');
})
});
I am doing something similar; I simply use the native Ext.Msg class
Controller code
,onDelete: function() {
var me = this;
Ext.Msg.show({
title:'Really shure?',
msg: 'Really wanna do this?',
buttons: Ext.Msg.YESNO,
icon: Ext.Msg.QUESTION,
closable: false,
fn: function(btn) {
if (btn == 'yes') {
me.deleteRecord();
}
},
scope: me
});
}
,deleteRecord: function() {
var me = this,
store = Ext.StoreMgr.lookup('datastore');
store.remove(me.selectedRecord);
store.sync();
}
I would recommend you to keep all logic concerning this within the controller. I your case it'seems that's no problem, cause you just catching the button-events. You problem may be that all controllers catch these, if you are using totally the same window.
You can solve this for example by creating the action property value dynamically when creating the window. Like action='onDeleteCar'
I think you should embed the 'confirmation' functionality inside the button, i.e. create your own ConfirmButton class that would first fire a dialog upon pressing and executing the passed handler only if the dialog exited with "yes".
Here is the example implementation:
Ext.define('My.ConfirmButton', {
extend: 'Ext.button.Button',
alias: 'widget.confirmbutton',
dlgConf: {
title: 'Are you sure?',
msg: 'Are you sure you want to delete this?',
buttons: Ext.Msg.YESNO,
closable: false
},
initComponent: function() {
this.callParent(arguments);
// remember the originally passed handler
this.origHandler = this.handler;
this.origScrope = this.scope;
// override current handler to fire confirmation box first
this.handler = this.confirmHandler;
this.scope = this;
},
confirmHandler: function(me, e) {
// show dialog and call the original handler only on 'yes'
Ext.Msg.show(Ext.applyIf({
fn: function(buttonId) {
if(buttonId == 'yes') {
me.origHandler && me.origHandler.call(me.origScope || me, me, e)
}
},
scope: me
}, this.dlgConf))
},
// Method used to dynamically reassign button handler
setHandler: function(handler, scope) {
// remember the originally passed handler
this.origHandler = this.handler;
this.origScrope = this.scope;
// override current handler to fire confirmation box first
this.handler = this.confirmHandler;
this.scope = this;
return this;
},
});
Here is the sample usage:
Ext.create('My.ConfirmButton', {
text: 'Delete me',
renderTo: Ext.getBody(),
handler: function() {
alert('Aww, you deleted something! :(')
}
});
As you see, the confirmation logic is hidden from the outside world, you use this button exactly like you would use a regular Ext.Button (by passing a handler to it). Also, you can override the configuration of the dialog that the button fires (you may want to adjust it to your needs, e.g. allow passing record name to the dialog for a friendlier UI).
Note that the code isn't thoroughly tested, some cases might be left uncovered.
UPD. You need to add an alias (former xtype) to the component class definition so you can use it in ComponentQuery in your controller code, e.g.
this.control({
'confirmbutton[action = delete]' : {
click : this.delete
},
'confirmbutton[action = notDelete]' : {
click : this.notDelete
}
})
The final solution that i used was to declare variables using the global namespace so that they can be accessed from anywhere. On opening the first form, i get the data from the form using the record variable, and assign them a global name like
App1.Var1 = record.data.id;
And, on opening the delete window, these variables can be accessed by App1.Var1 when the buttons are clicked.

Add a custom button in column header dropdown menus {EXTJS 4}

I want a button in column header dropdown menu of grid in extjs4.
so that i can add or delete columns which are linked in database.
Any help will be appreciated...
Thankyou..:)
Couple of months ago I had the same problem. I've managed to solve it by extending Ext.grid.header.Container (I've overrided getMenuItems method). However, recently, I've found another solution which requires less coding: just add menu item manualy after grid widget is created.
I'll post the second solution here:
Ext.create('Ext.grid.Panel', {
// ...
listeners: {
afterrender: function() {
var menu = this.headerCt.getMenu();
menu.add([{
text: 'Custom Item',
handler: function() {
var columnDataIndex = menu.activeHeader.dataIndex;
alert('custom item for column "'+columnDataIndex+'" was pressed');
}
}]);
}
}
});
Here is demo.​
UPDATE
Here is demo for ExtJs4.1.
From what I have been seeing, you should avoid the afterrender event.
Context:
The application I am building uses a store with a dynamic model. I want my grid to have a customizable model that is fetched from the server (So I can have customizable columns for my customizable grid).
Since the header wasn't available to be modified (since the store gets reloaded and destroys the existing menu that I modified - using the example above). An alternate solution that has the same effect can be executed as such:
Ext.create('Ext.grid.Panel', {
// ...
initComponent: function () {
// renders the header and header menu
this.callParent(arguments);
// now you have access to the header - set an event on the header itself
this.headerCt.on('menucreate', function (cmp, menu, eOpts) {
this.createHeaderMenu(menu);
}, this);
},
createHeaderMenu: function (menu) {
menu.removeAll();
menu.add([
// { custom item here }
// { custom item here }
// { custom item here }
// { custom item here }
]);
}
});
For people who would like to have not just one "standard" column menu but have an individual columnwise like me, may use the following
initComponent: function ()
{
// renders the header and header menu
this.callParent(arguments);
// now you have access to the header - set an event on the header itself
this.headerCt.on('menucreate', function (cmp, menu, eOpts) {
menu.on('beforeshow', this.showHeaderMenu);
}, this);
},
showHeaderMenu: function (menu, eOpts)
{
//define array to store added compoents in
if(this.myAddedComponents === undefined)
{
this.myAddedComponents = new Array();
}
var columnDataIndex = menu.activeHeader.dataIndex,
customMenuComponents = this.myAddedComponents.length;
//remove components if any added
if(customMenuComponents > 0)
{
for(var i = 0; i < customMenuComponents; i++)
{
menu.remove(this.myAddedComponents[i][0].getItemId());
}
this.myAddedComponents.splice(0, customMenuComponents);
}
//add components by column index
switch(columnDataIndex)
{
case 'xyz': this.myAddedComponents.push(menu.add([{
text: 'Custom Item'}]));
break;
}
}
I took #nobbler's answer an created a plugin for this:
Ext.define('Ext.grid.CustomGridColumnMenu', {
extend: 'Ext.AbstractPlugin',
init: function (component) {
var me = this;
me.customMenuItemsCache = [];
component.headerCt.on('menucreate', function (cmp, menu) {
menu.on('beforeshow', me.showHeaderMenu, me);
}, me);
},
showHeaderMenu: function (menu) {
var me = this;
me.removeCustomMenuItems(menu);
me.addCustomMenuitems(menu);
},
removeCustomMenuItems: function (menu) {
var me = this,
menuItem;
while (menuItem = me.customMenuItemsCache.pop()) {
menu.remove(menuItem.getItemId(), false);
}
},
addCustomMenuitems: function (menu) {
var me = this,
renderedItems;
var menuItems = menu.activeHeader.customMenu || [];
if (menuItems.length > 0) {
if (menu.activeHeader.renderedCustomMenuItems === undefined) {
renderedItems = menu.add(menuItems);
menu.activeHeader.renderedCustomMenuItems = renderedItems;
} else {
renderedItems = menu.activeHeader.renderedCustomMenuItems;
menu.add(renderedItems);
}
Ext.each(renderedItems, function (renderedMenuItem) {
me.customMenuItemsCache.push(renderedMenuItem);
});
}
}
});
This is the way you use it (customMenu in the column config let you define your menu):
Ext.define('MyGrid', {
extend: 'Ext.grid.Panel',
plugins: ['Ext.grid.CustomGridColumnMenu'],
columns: [
{
dataIndex: 'name',
customMenu: [
{
text: 'My menu item',
menu: [
{
text: 'My submenu item'
}
]
}
]
}
]
});
The way this plugin works also solves an issue, that the other implementations ran into. Since the custom menu items are created only once for each column (caching of the already rendered version) it will not forget if it was checked before or not.

Ext JS 4 - how to change content of an item which can't be referenced by an ID

I would really appreciate any help with the following problem:
I need to be able to change content of an item (div or textfield) but the problem is that there are going to be multiple instances of the same window so I cannot use div IDs.
I tried this little example:
var myBtnHandler = function(btn) {
myPanel.items.items[0].html = "Changed by click!";
myPanel.doLayout();
}
var fileBtn = new Ext.Button({
text : 'Change',
handler : myBtnHandler
});
var panel1 = {
html : 'Original content.'
};
var myPanel = new Ext.Window({
title : 'How to change it?',
items : [
panel1,
fileBtn
]
});
myPanel.items.items[0].html = "Changed on load!";
myPanel.show();
Referencing an element by myPanel.items.items[0] works on load but does not work when it's in the button handler - is it a scope-related problem? How to reference an element without its ID?
Thank you very much,
H.
The problem has nothing to do with scope. The first time you set the html property, the component has not yet been rendered, so on initial render it will read the html property off the component. The second time, you're just setting a property on an object, it's not going to react in any way.
Instead, you should use the update() method.
Ext.require('*');
Ext.onReady(function() {
var myBtnHandler = function(btn) {
myPanel.items.first().update("Changed by click!");
}
var fileBtn = new Ext.Button({
text: 'Change',
handler: myBtnHandler
});
var panel1 = {
html: 'Original content.'
};
var myPanel = new Ext.Window({
title: 'How to change it?',
items: [panel1, fileBtn]
});
myPanel.items.items[0].html = "Changed on load!";
myPanel.show();
});
There are several functions that go through elements that belongs to a container. Try using for example down():
btn = panel.down('button');
Where 'button' parameter would mean 'give me element which type is equal to 'button'. Check out Sencha doc for querying various elements too: http://docs.sencha.com/ext-js/4-0/#!/api/Ext.ComponentQuery
Following on from Sha's reply. To put his advice in context with your example.
var myBtnHandler = function(btn) {
btn.up('window').down('panel').update('Changed by click!');
}
var fileBtn = new Ext.Button({
text : 'Change',
handler : myBtnHandler
});
var panel1 = {
html : 'Original content.'
};
var myPanel = new Ext.Window({
title : 'How to change it?',
items : [
panel1,
fileBtn
]
});
myPanel.show();
myPanel.update('Changed on load!');

Ext.grid.Panel get selected record from tbar button

I created a View extend Ext.grid.Panel and also attach a tbar to it, in the toolbar I got 2 buttons [Add] and [Remove] in this question I am focus on the [Remove] command only.
As usual I want to get hold to current selected record in the grid which I want to delete.
so in the controller:
init: function() {
this.control({
'extendgridlist button[action=remove]': {
click: this.removeCurrentRow;
}
});
}
removeCurrentRow: function(t){
// how do i get current selected record
}
removeCurrentRow: function(t){
var grid = t.up('gridpanel');
var arraySelected =grid.getSelectionModel().getSelection();
//assuming you have a single select, you have the record at index 0;
var record = arraySelected[0]
}
The answer by 'nscrob' should work just fine, I just wanted to point out an alternate method. Every Ext component can have an 'id' field. So, if you gave your grid a config option like the following when it was created:
id:'my_grid_id'
Then, from anywhere including inside your removeCurrentRow function, you could do the following:
var grid = Ext.getCmp('my_grid_id');
var rows = grid.getSelectionModel().getSelection();
if(!rows.length)
{ //in case this fires with no selection
alert("No Rows Selected!");
return;
}
var row = rows[0];
As I said, similar to other answers, but just another way of accessing the grid.
In case grid is to selType = "cellModel" use code below:
var grid = Ext.getCmp('id-of-grid');
var recid = grid.getSelectionModel().getCurrentPosition();
if(recid && recid.row){
var r = grid.getStore().getAt(recid.row)
alert(r.data.anyofgridfieldid)
}
more details: http://as400samplecode.blogspot.com/2012/01/extjs-grid-selected-row-cell.html
...
xtype: 'button',
text: 'Edit',
handler: function() {
onEdit(this.up('theGrid')); // alias: 'widget.theGrid' in definition
}
...
function onEdit(theGrid) {
if (theGrid.getSelectionModel().hasSelection()) {
var rows = theGrid.getSelectionModel().getSelection();
var row = rows[0];
console.log('Count Rows Selected : ' + rows.length);
console.log('The Row : ' + row); // columns: 'id', 'name', 'age'
console.log('Student Id: ' + row.get('id'));
console.log('Student Name: ' + row.get('name'));
console.log('Student Age: ' + row.get('age'));
}
else {
console.log('No Row Selected.');
}
}