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

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.

Related

Dojo ListItem with Child Inputs

I have a dojo list item that is clickable.. But at the same time we like to put input elements inside the list item. The problem is that if you click on the child element(example checkbox) the listitem onclick intercepts the call first(which seems opposite of the html bubble up format). So we cannot call stoppropagation on the child element to stop the listitem from changing the page.
In the example below you will see the listitem alert come up before the checkbox alert..
How do you handle having input elements in a listitem without triggering the listitem..
fiddle::http://jsfiddle.net/theinnkeeper/HFA36/1/
ex.
var list1 = registry.byId("myList");
var item = new ListItem ({
label: "A \"programmatic\" ListItem",
moveTo: "#",
noArrow:true,
onClick : function() {
alert("listItem clicked !" + event.target.type);
}
});
list1.addChild(item);
var check = new cb({onClick:function(){alert("checkbox clicked");event.stopPropagation();}});
check.placeAt(item.containerNode.firstChild);
check.startup();
I had a similar problem a while back and noticed that the dojox/mobile/ListItem is not really great when adding extra event handlers to it (checkboxes, touch gestures, ...), so to solve that I usually extend dojox/mobile/ListItem and fix the events by myself.
For example:
var CheckedListItem = declare("dojox/mobile/CheckedListItem", [ ListItem ], {
_initializeCheckbox: function() {
this.checkbox = new CheckBox({
});
domConstruct.place(this.checkbox.domNode, this.containerNode.firstChild, "last");
this.checkbox.startup();
this.checkbox.onClick = this.onCheckboxClick;
},
onCheckboxClick: function() { },
_setOnCheckboxClickAttr: function(handler) {
this.onCheckboxClick = handler;
if (this.checkbox !== null && this.checkbox !== undefined) {
this.checkbox.onClick = handler;
}
},
_onClick: function(e) {
if (e.target !== this.checkbox.domNode) {
this.inherited(arguments);
}
},
postCreate: function() {
this.inherited(arguments);
this._initializeCheckbox();
}
});
Due to overriding _onClick() and adding additional checks I managed to get the intended behavior.
A full example can be found here: http://jsfiddle.net/LQ6Mb/

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/

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

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');

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 Tooltip only shows after first mousover event

I'm using dojo's event delegation to connect a Tooltip widget to dynamically generated dom nodes.
The Dojo site explains event delegation this way:
"The idea behind event delegation is that instead of attaching a
listener to an event on each individual node of interest, you attach a
single listener to a node at a higher level, which will check the
target of events it catches to see whether they bubbled from an actual
node of interest; if so, the handler's logic will be performed."
Following is my code implementation. It works beautifully ... EXCEPT, the tooltip only shows AFTER the first mouse over event. When I first mouseover the node, the event fires perfectly, but the tooltip doesn't render. It will only show the consequent mouseover events. On the first mouseover event, I can watch the Firebug console and see the xhr.get go to the database and get the correct data. If I comment out the tooltip and throw in a simple alert(), it works the first time.
Any suggestions on how to get the Tooltip to show on the first mouseover event? Thanks in advance!
<div class="col_section" id="my_groups">
<div class="col_section_label">My Groups</div>
<ul>
<?php
foreach($myGroups as $grp) {
echo '<li><a class="myGroupLink" id="grp'.$grp['grp_id'].'">'.$grp['name'].'</a></li>';
}
?>
</ul>
</div>
<script>
require(["dojo/on",
"dojo/dom",
"dijit/Tooltip",
"dojo/_base/xhr",
"ready!"], function(on, dom, Tooltip, xhr) {
// Get Group ToolTip
var myObject = {
id: "myObject",
onMouseover: function(evt){
var grp_id = this.id;
var content = '';
xhr.get({
url: "getGrpInfo.php",
handleAs: "json",
content: {
grp_id: grp_id,
content: "tooltip"
},
load: function(info) {
if(info == 0) {
content = '<div class="grpToolTip">';
content += ' Information about this group is confidential';
content += '</div>';
} else {
content = '<div class="grpToolTip">';
content += ' <img src="../ajax/getimg.php?id='+info.logo_id+'" />';
content += ' <div style="text-align:center">'+info.name+'</div>';
content += '</div>';
}
new Tooltip({
connectId: [grp_id],
label: content
});
},
error: function() {}
});
}
};
var div = dom.byId("my_groups");
on(div,".myGroupLink:mouseover",myObject.onMouseover);
});
</script>
Your Tooltip does not show on the first onmouseover because it does not exist at the moment the onmouseover event was fired.
dijit/Tooltip instances manage theirs mouse events themselves, so you do not have to manage onmouseover/onmouseout and you probably did so because you do not want to preload data or you want to load data every time the tooltip is about to show.
Beside dijit/Tooltip instances you can use Tooltip.show(innerHTML, aroundNode, position) and Tooltip.hide(aroundNode) to display tooltips, but in that case you will have to manage mouse events yourself, which is what you need, because from the UX perspective, you do not want to show single tooltip, you want to:
Show a tooltip indicating information is being loaded.
Then either:
display XHR loaded information if a user still hover over the node
cancel XHR and hide tooltip on mouseout
Here is working example: http://jsfiddle.net/phusick/3hmds/
require([
"dojo/dom",
"dojo/on",
"dojo/_base/xhr",
"dijit/Tooltip",
"dojo/domReady!"
], function(
dom,
on,
xhr,
Tooltip
) {
on(dom.byId("groups"), ".group-link:mouseover", function(e) {
var target = e.target;
Tooltip.show("Loading...", target);
var def = xhr.post({
url: "/echo/html/",
content: { html: target.textContent},
failOk: true,
load: function(data) {
Tooltip._masterTT.xhr = null;
Tooltip._masterTT.containerNode.innerHTML = data;
Tooltip._masterTT.domNode.width = "auto";
},
error: function(e) {
if (e.dojoType != "cancel") {
console.error(e);
}
}
});
Tooltip._masterTT.xhr = def;
});
on(dom.byId("groups"), ".group-link:mouseout", function(e) {
var target = e.target;
Tooltip.hide(target);
if (Tooltip._masterTT.xhr) {
Tooltip._masterTT.xhr.cancel();
}
});
});​
As usual, I was over-thinking the problem, focusing on event registration rather than on simply creating the tooltips when the page loads. So, it's really stupidly simple:
query for the nodes
iterate through them and create the tooltips pointing to each node.
var myGroupsList = query("a.myGroupLink"); // query nodes based on class
array.forEach(myGroupsList,function(entry,i){ // iterate through
var grp_id = entry.id;
var content = '';
xhr.get({ // get data via xhr.get
url: "getGrpInfo.php",
handleAs: "json",
content: {
grp_id: grp_id,
content: "tooltip"
},
load: function(info) {
if(info == 0) {
content = '<div class="grpToolTip">';
content += ' Information about this group is confidential';
content += '</div>';
} else {
content = '<div class="grpToolTip">';
content += ' <img src="../ajax/getimg.php?id='+info.logo_id+'" />';
content += ' <div style="text-align:center">'+info.name+'</div>';
content += '</div>';
}
new Tooltip({ // create tooltip
connectId: [entry.id],
label: content
});
},
error: function() {}
});
});