Extjs grid grouping summary click event - extjs

I'm using Ext.grid.Panel with Ext.grid.feature.GroupingSummary. i need to add listener for summary row click event. Is there are any event for summary row click.
Ext.create('Ext.grid.Panel', {
features:[
Ext.create('Ext.grid.feature.GroupingSummary',{
ftype: 'groupingsummary'
})
],

As far as I can tell, there's nothing built-in to do that. You will have to catch the click event on the summary element yourself. That remains relatively easy. Things get complicated if you need to know the group of the summary that has been clicked...
You can use the getGroupName method of the feature. For that, you'll need to keep a reference to the grouping feature instance and, the joyful part, you'll have to find the group header element that matches the clicked summary element. To spice things up even a little more, the markup for group and summary elements seems to have changed drastically in Ext 4.2.
Here's the code of a listener (on the click event of summary element) which does all that.
function(e, target) {
// Find group element (header), for the clicked summary
var groupEl;
if (Ext.getVersion().isLessThan('4.2')) {
// Life used to be easy with everything being a row
// in the table (actual rows, group headers,
// summary row)...
groupEl = Ext.fly(target).prev('.x-grid-group-hd');
} else {
// But from Ext4.2, everything became complicated.
// Group headers & summary row seem to be embedded
// in the next or previous regular row... Since I
// haven't entirely understood the logic behind, I
// cannot guarantee this will work with all possible
// cases...
var row = Ext.fly(target).up('.x-grid-row');
while (row && !groupEl) {
groupEl = row.down('.x-grid-group-hd');
row = row.prev('.x-grid-row');
}
}
// We can get the group name from the group element,
// but we need a reference to the grouping feature
// instance...
var groupName = groupingSummary.getGroupName(groupEl);
// Here you are...
console.log('Group clicked: ' + groupName);
}
And here's a complete example, based on the grouping summary grid example from the doc.
Ext.define('TestResult', {
extend: 'Ext.data.Model',
fields: ['student', 'subject', {
name: 'mark',
type: 'int'
}]
});
var groupingSummary = Ext.create('Ext.grid.feature.GroupingSummary', {
groupHeaderTpl: 'Subject: {name}',
ftype: 'groupingsummary'
});
Ext.create('Ext.grid.Panel', {
width: 200,
height: 240,
renderTo: document.body,
features: [groupingSummary],
store: {
model: 'TestResult',
groupField: 'subject',
data: [{
student: 'Student 1',
subject: 'Math',
mark: 84
},{
student: 'Student 1',
subject: 'Science',
mark: 72
},{
student: 'Student 2',
subject: 'Math',
mark: 96
},{
student: 'Student 2',
subject: 'Science',
mark: 68
}]
},
columns: [{
dataIndex: 'student',
text: 'Name',
summaryType: 'count',
summaryRenderer: function(value){
return Ext.String.format('{0} student{1}', value, value !== 1 ? 's' : '');
}
}, {
dataIndex: 'mark',
text: 'Mark',
summaryType: 'average'
}]
,listeners: {
click: {
element: 'body'
,delegate: '.x-grid-row-summary'
,fn: function(e, target) {
// Find group element (header), for the clicked summary
var groupEl;
if (Ext.getVersion().isLessThan('4.2')) {
// Life used to be easy with everything being a row
// in the table (actual rows, group headers,
// summary row)...
groupEl = Ext.fly(target).prev('.x-grid-group-hd');
} else {
// But from Ext4.2, everything became complicated.
// Group headers & summary row seem to be embedded
// in the next or previous regular row... Since I
// haven't entirely understood the logic behind, I
// cannot guarantee this will work with all possible
// cases...
var row = Ext.fly(target).up('.x-grid-row');
while (row && !groupEl) {
groupEl = row.down('.x-grid-group-hd');
row = row.prev('.x-grid-row');
}
}
// We can get the group name from the group element,
// but we need a reference to the grouping feature
// instance...
var groupName = groupingSummary.getGroupName(groupEl);
// Here you are...
console.log('Group clicked: ' + groupName);
}
}
}
});
The goal of this answer is just to demonstrate the principles. You may want to organize this code in a better way... The cleanest would probably be to extend or override the GroupingSummary class.

Related

Input field doesn't get cleared after choosing the value in the ExtJS tag field

I have the ExtJS tag field with anyMatch = true. Now if you type AB it will show the result and once you choose the selection it will clear the input you have entered i.e. AB Now when you have anyMatch= true that time if I type HI it will show you the result but when you choose the value the input field doesn't get cleared. I saw the ExtJS Tag field code it is handled explicitly in clearInput method. I wanted to know why this is implemented in this way ?
Below is the sample code
Ext.create('Ext.form.Panel', {
title: 'Tag Field Example',
width: 1000,
bodyPadding: 10,
items: [{
xtype: 'fieldcontainer',
labelWidth: 100,
layout: 'hbox',
items: [{
xtype: 'fieldcontainer',
defaults: {
flex: 1,
},
layout: 'hbox',
items: [{
xtype: 'tagfield',
minChars: 1,
anyMatch: true,
allowBlank: true,
margin: '5 5 5 5',
fieldLabel: 'Tag Field 1',
name: 'tagField1',
store: ['ABC D', 'EFG HI', 'C'],
queryMode: 'local',
filterPickList: true,
emptyText: 'Multi Select...'
}]
}]
}],
renderTo: Ext.getBody()
});
This seems to be a bug. If you take a look at the clearInput method from the tagfield class definition, and specifically at the section with the early return:
if (!Ext.String.startsWith(lastDisplayValue, inputValue, true)) {
return;
}
You can see that they discard clearing of the field if the last selected tag field value does not start with the typed input value ('abc d' starts with 'ab' so the field is cleared; 'efg hi' does not start with 'hi' - so the clearing is discarded).
This will clearly not work when you have the anyMatch config enabled.
The early return section from above, should be something like this:
if (!me.anyMatch) {
if (!Ext.String.startsWith(lastDisplayValue, inputValue, true)) {
return;
}
} else {
if (lastDisplayValue.toLowerCase().indexOf(inputValue.toLowerCase()) === -1) {
return;
}
}
We keep the initial check when anyMatch is not enabled, otherwise, we check if the typed input values is included in the last selected tag field value.
Here is a fiddle with the proposed override: https://fiddle.sencha.com/#view/editor&fiddle/32q0
I used arrays for multi string values (list item: Lincoln Abraham, input value: Abraham Lin).
That way method to checks if it matches properly.
In my implenmentation last part of the input string is used as wildcarded string. It also solves the problem where list item is reversed compare to input string, which was in my case.
clearInput: function() {
var me = this,
valueRecords = me.getValueRecords(),
inputValue = me.inputEl && me.inputEl.dom.value,
lastDisplayValue;
if (valueRecords.length && inputValue) {
lastDisplayValue = valueRecords[valueRecords.length - 1].get(me.displayField);
let inputValueArr = inputValue.split(' ');
let lastDisplayValueArr = lastDisplayValue.split(' ');
let matchCount = 0;
Ext.each(inputValueArr, function(iv, idx1, arr1) {
Ext.each(lastDisplayValueArr, function(ldv, idx1, arr2) {
if (!me.anyMatch) {
if (Ext.String.startsWith(ldv, iv, true)) {
matchCount++;
}
} else {
if (ldv.toLowerCase().indexOf(iv.toLowerCase()) !== -1) {
matchCount++;
}
}
});
});
if (matchCount < inputValueArr.length) {
return;
}
me.inputEl.dom.value = '';
if (me.queryMode === 'local') {
me.clearLocalFilter();
// we need to refresh the picker after removing
// the local filter to display the updated data
me.getPicker().refresh();
}
}
}

angular-ui ng-grid How to make the aggregate row be an editable row

I think that what I'm trying to achieve is having a tree-like inside the ng-grid. I didn't found such an implementation but I'm wondering if I can use the grouping mechanism
I need to have the group header be editable in the same manner as the rows below it (see image above), with exactly the same editable cells, acting as a master row. When updating one cell from the header group should update all the cells beneath that group.
From ng-grid docs http://angular-ui.github.io/ng-grid/ :
default value for aggregateTemplate:
<div ng-click="row.toggleExpand()" ng-style="{'left': row.offsetleft}" class="ngAggregate">
<span class="ngAggregateText">{{row.label CUSTOM_FILTERS}} ({{row.totalChildren()}} {{AggItemsLabel}})</span>
<div class="{{row.aggClass()}}"></div>
</div>
Is it possible to use this option in order to render the aggregate row as I described?
The below answer/comment is related to tree like structure and not related to making aggregate row editable...
If you are looking for tree-like structure in ng-grid, then you could achieve that with the combination of ng-if, ng-click and API(s) that updates the ng-grid data option on click of a particular row. Here is a sample plnkr.
On click of a parent row, a toggle function is called to add/remove child rows in to the ng-grid data. (Refer to my plunker code for complete details)
$scope.toggleDisplay = function(iType) {
$scope.displayItemDetails[iType] = $scope.displayItemDetails[iType] ? 0 : 1;
$scope.selItems = $scope.updateTable();
};
$scope.updateTable = function() {
var selItems = [];
for (var i in $scope.allItems) {
var iType = $scope.allItems[i]["Type"];
if (angular.isUndefined($scope.displayItemDetails[iType])) {
$scope.displayItemDetails[iType] = 0;
}
if (1 == $scope.displayItemDetails[iType]) {
$scope.allItems[i]["Summary"] = '-';
} else {
$scope.allItems[i]["Summary"] = '+';
}
selItems.push($scope.allItems[i]);
if ($scope.displayItemDetails[iType]) {
for (var j in $scope.allItems[i]["Details"]) {
$scope.allItems[i]["Details"][j]["Summary"] = "";
selItems.push($scope.allItems[i]["Details"][j]);
}
}
}
return selItems;
};
$scope.gridOptions = {
data: 'selItems',
columnDefs: [{
field: 'Summary',
displayName: '',
cellTemplate: summaryCellTemplate,
width: 30
}, {
field: 'Name',
displayName: 'Name',
}, {
field: 'Type',
displayName: 'Type',
}, {
field: 'Cost',
displayName: 'Cost',
}, {
field: 'Quantity',
displayName: 'Quantity',
}],
enableCellSelection: false,
enableColumnResize: true
};

Delete row within sortable: true in Grid with Local Data

I try to make a gridpanel with local data http://jsfiddle.net/8um4T/
This grid has add and delete within sortable: true in column, I will delete record by id (but my way fail with large data)
Here is my data
var simpleData = [];
var store = new Ext.data.ArrayStore({
fields: ['id', 'name',],
data: simpleData
});
for (i = 0; i < 20; i++) {
simpleData.push({id:''+i+'', name: 'name'+i});
}
store.loadData(simpleData);
My tbar with a add button
tbar:[
{
text:'Add',
handler:function(){
simpleData.push({id:'x', name: 'name'});
store.loadData(simpleData);
}
}
]
My action column
{
header: '',
xtype: 'actioncolumn'
, width: 50
, items: [{ // Delete button
icon: 'http://whatisextjs.com/BAHO/icons/cancel.png',
tooltip: 'Delete'
, handler: function(grids, rowIndex, colindex) {
var record = grid.getStore().getAt(rowIndex);
//Delete item in array
// if data is large will don't working
Ext.each(simpleData, function (items, idx) {
if (items.id == record.data.id) {
simpleData.splice(idx, 1);
}
});
//Delete record in store
grid.store.removeAt(rowIndex);
}
}]
}
if mydata is small then delete button will working. My data in my example is 20 record and that don't work
my idea is remove record from store after assign for simpleData. But how to do that or has another anyway to fix my problem thanks
//grid.store.removeAt(rowIndex);
// simpleData = grid.store.data; // my idea is (but how)
You need to break the loop after splicing the array by returning false. You are taking away one of the elements but the loop is not aware of it. So the last element will be undefined.
The loop will fail because the last 'items' parameter is not an object. So "items" is not an object and does not have an id.
The result is that you are removing that object from the array but you never get to the grid.store.removeAt(rowIndex); sentence. I would do:
Ext.each(simpleData, function (items, idx) {
if (items.id == record.data.id) {
simpleData.splice(idx, 1);
return false;
}
});

Updating a value on an Ext.form.Panel

The following should be trivial, but I cannot get it to work:
I have the following Ext.form.Panel:
Ext.define('EvaluateIt.view.SiteEvaluationForm', {
extend: 'Ext.form.Panel',
alias : 'widget.siteEvaluationForm',
id: 'evaluationId',
requires: [
'Ext.form.Panel',
'Ext.form.FieldSet',
'Ext.field.Url',
'Ext.field.Select',
'Ext.field.Hidden'
],
config: {
// We give it a left and top property to make it floating by default
left: 0,
top: 0,
// Make it modal so you can click the mask to hide the overlay
modal: true,
hideOnMaskTap: true,
// Set the width and height of the panel
//width: 400,
//height: 330,
width: Ext.os.deviceType == 'Phone' ? screen.width : 300,
height: Ext.os.deviceType == 'Phone' ? screen.height : 500,
scrollable: true,
layout: {
type: 'vbox'
},
defaults: {
margin: '0 0 5 0',
labelWidth: '40%',
labelWrap: true
},
items: [
{
xtype: 'textfield',
name: 'address',
label: 'Address',
itemId: 'address'
},
{
xtype: 'hiddenfield',
itemId: 'imageUriId',
name: 'imageUri'
},
{
xtype: 'button',
itemId: 'siteImage',
text: 'Take Photo'
},
{
xtype: 'button',
itemId: 'save',
text: 'Save'
}
]
}
});
Which gets opened from an onItemDisclosure in a list view, and thus has a record bound to it.
When the 'siteImage' button is tapped, the user selects an image from the photo gallery and the uri is written to a temporary store for processing. This part works just fine.
What I need to do: When 'save' in the above form is tapped I need to take the uri from the temporary store and write it to the same store that all of the values from the above form get saved to.
To do this, I have the following method:
onSaveSiteEvaluation: function(button) {
console.log('Button Click for Save');
//var form = button.up('panel');
var form = Ext.getCmp('evaluationId');
//get the record
var record = form.getRecord();
//get the form values
//var values = form.getValues();
// return a clone for updating of values
var values = Ext.clone(form.getValues());
//if a new siteEvaluation
if(!record){
var newRecord = new EvaluateIt.model.SiteEvaluation(values);
Ext.getStore('SiteEvaluations').add(newRecord);
}
//existing siteEvaluation
else {
// get image uri from temp store
var images = Ext.create('EvaluateIt.store.ImageQueue');
images.queryBy(function(record,id){
images = Ext.getStore(images);
if (images.getCount() > 0) {
var uri = record.get('src');
// image = Ext.getCmp('imageUri');
//image = form.setValue(uri);
//form.getCmp('imageId').setValue(uri);
console.log('URI: ' + uri);
// THIS DOES NOT WORK!!
form.setValues({
imageUri: uri
})
//record.set('imageUri',uri)
console.log('imageUri: '+ record.get('imageUri'));
}
});
// do stuff
record.set(values);
}
form.hide();
//save the data to the Web local Storage
Ext.getStore('SiteEvaluations').sync();
},
Everything in this method works EXCEPT where I write the value of the uri to the form
form.setValues({
imageUri: uri
})
I've tried making 'uriImage' as an xType of hiddenfield and textfield, I've tried cloning the values from the form, etc. all with absolutely no luck in updating the actual attribute imageUri in the store (NOTE: All other form values are updated just fine). What am I missing? Thanks!
UPDATE
This works:
images.queryBy(function(iRecord,id){
images = Ext.getStore(images);
if (images.getCount() > 0) {
var uri = iRecord.get('src');
// update store with URI
form.setValues({
imageUri: uri
})
values = form.getValues();
record = form.getRecord();
}
});
// do stuff
record.set(values);
All's well that ends well!
Because Ext.form.Panel doesn't have setValue method. You first need to get basic form out of it:
form.getForm().setValue();
UPDATE: My bad, I was looking at the ExtJs docs and not Sencha Touch. your form does have setValue method.
After you call setValues(), can you go getRecord() again? Also looks like your record internal variable is defined twice. That should not be an issue, but...

EXTJS checkcolumns disable on click

I have two EXT JS check columns like this:
var maleColumn = new Ext.grid.CheckColumn({
dataIndex: 'male',
headerId: 'male'
})
maleColumn .on('click', function(s, e, t, record) {...})
var femaleColumn = new Ext.grid.CheckColumn({
dataIndex: 'female',
headerId: 'female'
})
femaleColumn .on('click', function(s, e, t, record) {...})
Now I need to write an onClick event so that when one of these is clicked, the other one is disabled. How can I do it?
First, I should say that's a bit strange to have two CheckColumn for gender. Why not having one Column that has a combobox for user to select the gender?
But if you have to do so, I have a solution. My solution is based on Extjs 3.2.1.
//First, extend Ext.ux.grid.CheckColumn
MyCheckColumn = Ext.extend(Ext.ux.grid.CheckColumn,{
//The reference to the related dataIndex
relatedIndex : null,
//Override onMouseDown method
onMouseDown : function(e, t){
if(Ext.fly(t).hasClass(this.createId())){
e.stopEvent();
var index = this.grid.getView().findRowIndex(t);
var record = this.grid.store.getAt(index);
/*
* By checking the related record data, we can know if the other CheckColumn
* is checked or not (true/false means checked/unchecked).
* If false, we can then check the checkbox that is clicked.
*/
if(!record.data[this.relatedIndex])
record.set(this.dataIndex, !record.data[this.dataIndex]);
}
}
});
//Using MyCheckColumn and include relatedIndex in the config options.
var maleColumn = new MyCheckColumn({
dataIndex: 'male',
relatedIndex: 'female',
headerId: 'male'
});
var femaleColumn = new MyCheckColumn({
dataIndex: 'female',
relatedIndex: 'male',
headerId: 'female'
});
Though the solution works but I don't recommend it since the implementation may change when extjs upgrades. e.g. For extjs 3.3.1, you have to override another method but not onMouseDown:
processEvent : function(name, e, grid, rowIndex, colIndex){
if (name == 'mousedown') {
var record = grid.store.getAt(rowIndex);
//Do the changes here like the way I do above...
record.set(this.dataIndex, !record.data[this.dataIndex]);
return false; // Cancel row selection.
} else {
return Ext.grid.ActionColumn.superclass.processEvent.apply(this, arguments);
}
}

Resources