Dojo DropDownButton, can I differentiate between a click on button vs. down arrow? - dojo

I'm using Dojo to create a DropDownButton within a Toolbar. The Toolbar, and button are created dynamically, like this:
this.widget = new Toolbar({ style: "background:black;" }, "toolbar");
this.dropMenu = new DropDownMenu({tooltip : "ToolTip", style: "display: none;"});
this.button = new DropDownButton({dropDown: this.dropMenu});
this.button.set('label', '<img src="data:image/png;base64,'+ this.icon + '"/>');
this.widget.addChild(this.button);
Note that the above code is dynamically creating an icon as part of the button from a base64 encoded string through setting an img src for the label property of the button.
I want to differentiate between a click on the "label" element for the DropDownButton and a click on the down arrow for the button, but am not sure if this is possible. Ie, when clicking on the label, I capture the onClick, but don't cause the drop down to be displayed. However, if the down arrow is clicked on or any other place on the button is clicked, the drop down will be displayed.
One alternate would be to split this into a standard Button, and then a drop down button adjacent to it, but I'm wondering if there is any way to do this from a single standard DropDownButton?

Check whether or not its the downarrow or buttontext class in the clicked element. To properly hook into the 'flow' of events, you should override the classfunction _onDropDownMouseDown
var customDropDownButton = declare("customDropDownButton", [ DropDownButton ], {
toggleDropDown: function() {
console.log('toggling');
this.inherited(arguments);
},
_onDropDownMouseDown: function(evt) {
console.log(arguments, evt.srcElement.className);
if (/dijitButtonText/.test(evt.srcElement.className)) {
// negate popup functionality
console.log('negating');
return false;
}
this.inherited(arguments);
return true;
}
});
var b = new customDropDownButton({
label: "hello!",
name: "programmatic1",
dropDown: someMenu
});
Alternatively, if you can live with popup showing and then immediately closing again - easy way is:
var b = new DropDownButton({
label: 'hello!',
name: "programmatic2",
dropDown: someMenu,
onClick: function(evt) {
if(/dijitButtonText/.test(evt.srcElement.className)) {
// negate popup
popup.close(this.dropDown);
}
}
}, 'button');

Related

ag-grid: how to add button in master detail template

I am using master/detail in ag-grid. I want a save button in detailed section after all rows. I am able to do that using template, but its not firing the event. I think its just a template and don't work for event. Can you please suggest me how can I add button in detailed section
here is my code
columnDefs: [
{
headerName: "CompanyName",
field: "CompanyName",
cellRenderer: "agGroupCellRenderer"
}
],
groupDefaultExpanded: 0,
detailRowHeight: 200,
detailCellRendererParams: {
detailGridOptions: {
columnDefs: [
{
headerName: "Name",
field: "Name",
cellRenderer: "agGroupCellRenderer"
},
{
headerName: "Age",
field: "Age",
suppressFilter: true,
},
{
headerName: "Gender",
field: "Gender",
suppressFilter: true,
}
],
onGridReady: function (params: any) {
params.api.sizeColumnsToFit();
}
},
getDetailRowData: function (params: any) {
params.successCallback(params.data.ChannelComponentsVm);
},
template:
'<div style="height: 100%;">' +
'<div ref="eDetailGrid" style="height: 90%;"></div>' +
'<div style="height: 10%;"> <button (click)="saveEmployeeDetails()">Save</button> </div>' +
'</div>'
}
I cannot use it in cell renderer as the button is not in column. It should be at the end of the child grid.
-> parent row
-> child row1
-> child row2
-> child row3
Save button
How to add save button at the end of detailed rows
// get ag grid template ref
#ViewChild('agGrid') agGrid: TemplateRef < any > ;
// attach the click listener on row group opened grid event.
// don't forget to clean up this listener on destroy component event or somewhere in the code
rowGroupOpened(params: any) {
var saveButton = this.agGrid["_nativeElement"].querySelector(`#saveDetailsButton`);
if (saveButton) {
saveButton.addEventListener('click',this.saveEmployeeDetails.bind(this));
}
}
saveEmployeeDetails(): void {
console.log('save button clicked');
}
vinoth's answer has the right idea, but I wanted to give a more complete answer shown a slightly different way. To start with, I had the same issue with the event not firing - this is due to lifecycle events and how things are compiled. If you are using a template (as shown in the question) then you cannot put the onClick event in the HTML in the template, you have to instead do it in TypeScript/JavaScript in the .ts file.
In your HTML where you define the ag-grid div, assign the action method rowGroupOpened. This should be in your HTML file, or possibly in your .ts file (not in the template in grid definitions).
(rowGroupOpened)="rowGroupOpened($event)"
In your .ts file, define the rowGroupOpened event.
RowGroupOpened(params: any) {
//below pass the id of your button/div used in your template
const buttonDiv = document.getElementById('saveButton');
if (buttonDiv){
buttonDiv.addEventListener('click', (e) => {
this.myButtonClickMethod();
});
}
}
myButtonClickMethod() {
//do something
}
Your button in the template string should have an id like this:
<button id="saveButton">Save</button>
This way when one of your detail grids expanded it will attach the onClick method at that time. Remember that by default, if a detail grid is not expanded then it doesn't exist/is undefined. Also don't forget to do cleanup/destroy when needed.

Click event not firing on MenuItem with submenu

In my nw.js app I have a menubar with a 'Parent' menuitem. 'Parent' has both a click event and a submenu. I noticed the click event on parent does not trigger when there is a submenu. Is there any way to capture the click event on the parent menuitem of the submenu? Or is this simply expected behavior?
var menubar = new window.gui.Menu({ type: 'menubar' });
var fileMenu = new window.gui.Menu();
fileMenu.append(new window.gui.MenuItem({
label: 'New',
click: function() {
window.alert("New");
}
}));
fileMenu.append(new window.gui.MenuItem({
label: 'Open',
click: function() {
window.alert("Open");
}
}));
menubar.append(new window.gui.MenuItem({
label: 'Parent',
submenu: fileMenu,
click: function() {
window.alert("Does not fire when submenu set");
}
}));
window.win.menu = menubar;
This is expected behavior. In other desktop applications, you also don't see a menu item without a submenu attached to it...

dojo How to dynamically change the title of the tab in tabContainer?

If I have made to the tab through the dojo,
mycode
var tab=new dijit.layout.ContentPane({
title:formCount,
id:''+formCount,
content:cont,
class:'tab',
closable: true,
onClose:function(){
return confirm('Relly want to remove?');
}
});
dijit.byId('tabContainer').addChild(tab);
After the tab is created, i want to change the tab title dynamically through dijit/Dialog.
but I don't know how it should be implemented,Please advise me
The best way to achieve this is to create your own widget and extend from dijit/layout/ContentPane, for example:
declare([ ContentPane ], {
});
Then you can add stuff to show a dialog, for example:
declare([ ContentPane ], {
btn: domConstruct.create("button", {
innerHTML: "Change title",
}),
_createButton: function() {
domConstruct.place(this.btn, this.domNode);
on(this.btn, "click", lang.hitch(this, this._showDialog));
},
postCreate: function() {
this.inherited(arguments);
this._createButton();
this._createDialog();
}
});
The postCreate function is a part of the widget lifecycle in Dojo and is automatically executed when the widget is loaded. So, we use that to add a "Change title" button to the contentpane that, when being clicked calls a function this._showDialog() (that's what you can see in this._createButton()).
Of course, you also need to create a dijit/Dialog before you can actuall show one, so you could do something like:
declare([ ContentPane ], {
/** ... */
dialog: null,
editField: null,
okBtn: null,
_showDialog: function() {
this.editField.value = this.title;
this.dialog.show();
},
_createDialog: function() {
var div = domConstruct.create("div");
domConstruct.place(div, this.domNode);
this.dialog = new Dialog({
title: "Change title",
content: ""
}, div);
this.editField = domConstruct.create("input", {
type: "text"
});
this.okBtn = domConstruct.create("button", {
innerHTML: "OK"
});
domConstruct.place(this.editField, this.dialog.containerNode);
domConstruct.place(this.okBtn, this.dialog.containerNode);
on(this.okBtn, "click", lang.hitch(this, this._saveTitle));
},
/** .. */
});
What happens here is that we create a dialog with a simple textfield and a button (the OK button), all of that can be found in this._createDialog().
In this._showDialog() you can see that I'm first changing the value of the textfield into the title of the contentpane. Then I show the dialog we made earlier.
Now all you have to do is read that value when the OK button is pressed:
declare([ ContentPane ], {
/** ... */
_saveTitle: function() {
this.set("title", this.editField.value);
this.dialog.hide();
},
/** ... */
});
That's all you really need. You can find a working example on JSFiddle: http://jsfiddle.net/LE49K/

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.

Dojo Exception on hiding a dijit.Dialog

I have a Dialog with a form inside. The following code is just an example of what I'm trying to do. When you close a dijit.Dialog, if you dont't destroy recursively his children, you just can't reopen it (with the same id).
If you don't want to destroy your widget you can do something like that :
var createDialog = function(){
try{
// try to show the hidden dialog
var dlg = dijit.byId('yourDialogId');
dlg.show();
} catch (err) {
// create the dialog
var btnClose = new dijit.form.Button({
label:'Close',
onClick: function(){
dialog.hide();
}
}, document.createElement("button"));
var dialog = new dijit.Dialog({
id:'yourDialogId',
title:'yourTitle',
content:btnClose
});
dialog.show();
}
}
I hope this can help but with this code the error thrown is :
exception in animation handler for: onEnd (_base/fx.js:153)
Type Error: Cannot call method 'callback' of undefined (_base/fx.js:154)
I have to say I'm a little lost with this one ! It is driving me crazy ^^
PS : sorry for my "French" English ^^
I'll introduce you to your new best friend: dojo.hitch()
This allows you to bind your onClick function to the context in which it was created. Chances are, when you push the button in your code, it is calling your .show() .hide() form the context of the global window. var dlg was bound to your createDialog function, so it's insides are not visible to the global window, so the global window sees this as undefined.
Here's an example of what I changed to your code:
var createDialog = function(){
// try to show the hidden dialog
var dlg = dijit.byId('yourDialogId');
dlg.show();
// create the dialog
var btnClose = new dijit.form.Button({
label:'Close',
onClick: function(){
dojo.hitch(this, dlg.hide());
}
}, document.createElement("button"));
dlg.domNode.appendChild(btnClose.domNode);
var btnShow = new dijit.form.Button({
label : 'Open',
onClick : function() {
dojo.hitch(this, dlg.show());
}
}, document.createElement("Button"));
dojo.body().appendChild(btnShow.domNode);
};
dojo.ready(function() {
createDialog();
});
Note the use of dojo.hitch() to bind any future calls or clicks of the various buttons to the context in which the dlg was created, forever granting the button's onclick method access to the inside of the createDialog function, where var dlg exists.
hi if i understand correctly, you didn't need to destroy dijit.Dialog every time. E.g.:
HTML: define simple button:
<button id="buttonTwo" dojotype="dijit.form.Button" onclick="showDialog();" type="button">
Show me!
</button>
Javascript:
// required 'namespaces'
dojo.require("dijit.form.Button");
dojo.require("dijit.Dialog");
// creating dialog
var secondDlg;
dojo.addOnLoad(function () {
// define dialog content
var content = new dijit.form.Button({
label: 'close',
onClick: function () {
dijit.byId('formDialog').hide();
}
});
// create the dialog:
secondDlg = new dijit.Dialog({
id: 'formDialog',
title: "Programatic Dialog Creation",
style: "width: 300px",
content: content
});
});
function showDialog() {
secondDlg.show();
}
See Example and reed about dijit.dialog