Reputation: 464
After posting the question ExtJS checkcolumn grid - check columns to left, uncheck columns to right and thinking there were existing questions and answers for a "select all" option, I've read a little deeper and they don't actually cover what I need in relation to my other question's answer.
I need to know what code is required to generate a checkbox in each column header that, when selected/deselected, changes the checkboxes in the given column.
Existing code for reference:
Ext.Loader.setConfig({
enabled: true
});
Ext.Loader.setPath('Ext.ux', '../ux');
Ext.require([
'Ext.ux.CheckColumn'
]);
Ext.onReady(function(){
Ext.define('ProcessModel', {
extend: 'Ext.data.Model',
fields: [
'Item',
'Phase1',
'Phase2',
'Phase3',
'Phase4',
'Phase5',
'Phase6',
'Phase7',
'Phase8',
'Phase9',
'Phase10'
]
});
// create the Data Store
var processStore = Ext.create('Ext.data.Store', {
model: 'processModel',
autoLoad: true,
proxy: {
// load using HTTP
type: 'ajax',
url: '<?= $site_url ?>/Production/Processes/<?= $itemId ?>',
reader: {
type: 'json',
model: 'ProcessModel',
root: data
}
}
});
function onCheckChange (column, rowIndex, checked, eOpts) {
var record = processStore.getAt(rowIndex);
var columnIndex = column.getIndex();
for (var i = 1; i <= 10; i++) {
if(checked) {
if (i <= columnIndex) {
record.set('Phase'+i, true);
}
else
{
record.set('Phase'+i, false);
}
}
else {
if (i >= columnIndex) {
record.set('Phase'+i, false);
}
}
}
}
Ext.create('Ext.grid.Panel', {
width: 800,
store: processStore,
title: 'Processes',
tbar: [
{
xtype: 'button',
text: 'Update',
handler: function(){
//TODO: update by POST function
}
}
],
columns: [
{
text: 'Item',
dataIndex: 'Item'
},{
xtype: 'checkcolumn',
text: 'Phase 1',
dataIndex:'Phase1',
listeners: {
checkChange: onCheckChange
}
},{
xtype: 'checkcolumn',
text: 'Phase 2',
dataIndex:'Phase2',
listeners: {
checkChange: onCheckChange
}
},{
xtype: 'checkcolumn',
text: 'Phase 3',
dataIndex:'Phase3',
listeners: {
checkChange: onCheckChange
}
},{
xtype: 'checkcolumn',
text: 'Phase 4',
dataIndex:'Phase4',
listeners: {
checkChange: onCheckChange
}
},{
xtype: 'checkcolumn',
text: 'Phase 5',
dataIndex:'Phase5',
listeners: {
checkChange: onCheckChange
}
},{
xtype: 'checkcolumn',,
listeners: {
checkChange: onCheckChange
}
text: 'Phase 6',
dataIndex:'Phase6',
listeners: {
checkChange: onCheckChange
}
},{
xtype: 'checkcolumn',
text: 'Phase 7',
dataIndex:'Phase7',
listeners: {
checkChange: onCheckChange
}
},{
xtype: 'checkcolumn',
text: 'Phase 8',
dataIndex:'Phase8',
listeners: {
checkChange: onCheckChange
}
},{
xtype: 'checkcolumn',
text: 'Phase 9',
dataIndex:'Phase9',
listeners: {
checkChange: onCheckChange
}
},{
xtype: 'checkcolumn',
text: 'Phase 10',
dataIndex:'Phase10',
listeners: {
checkChange: onCheckChange
}
}
],
renderTo: Ext.get('sencha_processes')
});
});
Imagined pseudo-code to handle select all function, for the kind of effect I'm looking for:
function selectAllInColumn (column, checked, eopts){
var columnIndex = column.getIndex();
for( var i = 0; i < processStore.getCount(); i++)
{
if(checked)
{
var record = processStore.getAt(i);
for(var j = 1; j <= columnIndex; j++) {
record.set('Phase'+columnIndex, true);
}
for(var j = columnIndex+1; j <= 10; j++) {
record.set('Phase'+columnIndex, false);
}
}
else
{
var record = processStore.getAt(i);
for(var j = columnIndex; j <= 10; j++) {
record.set('Phase'+columnIndex, false);
}
}
}
}
Upvotes: 2
Views: 9225
Reputation: 11167
A plugin solution usable for ExtJS 6.
Here's how to use it.
{
xtype: 'checkcolumn',
text: 'Selected',
dataIndex: 'selected',
plugins: {
ptype: 'selectallcheckcolumnheader',
checked: false // initial state of the checkbox
},
sortable: false // for better UX*
}
Here's how to install it.
Ext.define('MyApp.ux.plugin.SelectAllCheckColumnHeader', {
extend: 'Ext.AbstractPlugin',
alias: 'plugin.selectallcheckcolumnheader',
init: function (cmp) {
/*
* @cfg checked Boolean initial state of checkbox. Defaults to false
*/
var me = this;
me.checked = !!me.checked;
// class that make the div look like a checkbox
me._checkClass = Ext.baseCSSPrefix + 'grid-checkcolumn'
cmp.afterText = function(out, values) {
out.push(
'<div class="', me._checkClass, '" src="' + Ext.BLANK_IMAGE_URL + '"></div>'
)
}
// Position the checkbox
// - Make sure that checkbox show right of the header text instead of under it
// - Also horizontal align the checkbox better
cmp.cls = 'select-all-checkcolumn-header'
Ext.util.CSS.createStyleSheet([
'.', cmp.cls, ' .', Ext.baseCSSPrefix, 'column-header-text {',
' display: inline;',
'}',
'.', cmp.cls, ' .', me._checkClass, ' {',
' display: inline;',
' padding-left: 5px;',
'}',
].join(''), 'plugin_selectallcheckcolumnheader');
// initial display of checkbox or not based on me.checked
cmp.on('render', me.checkCheckbox, me);
// listen to header clicks, but only handle the onces exactly on the checkbox
cmp.on('headerclick', me.onHeaderClick, me);
// event fired by clicking on the checkbox and (un)checks all checkboxes in the grid
cmp.on('selectall', me.onSelectAll, me);
},
onHeaderClick: function(headerCt, header, e, clickedElement) {
var me = this;
var cmp = me.cmp;
var grid = headerCt.grid;
if (!Ext.get(clickedElement).hasCls(me._checkClass)) {
// It was not the particular checkbox that was clicked inside the header.
// The column header should proceed doing other stuff like sorting
return
}
me.checked = !me.checked;
cmp.fireEvent('selectall', grid.getStore(), header, me.checked);
me.checkCheckbox()
},
checkCheckbox: function() {
var me = this;
var cmp = this.cmp;
var checkboxEl = cmp.getEl().down('.' + me._checkClass);
checkboxEl[me.checked ? 'addCls' : 'removeCls'](Ext.baseCSSPrefix + 'grid-checkcolumn-checked');
},
onSelectAll: function(store, column, checked) {
var dataIndex = column.dataIndex;
var recordCount = store.getCount();
for (var i = 0; i < recordCount; i++) {
var record = store.getAt(i);
record.set(dataIndex, checked);
}
}
});
And don't forget to require the plugin in your component.
requires: [
'MyApp.ux.plugin.SelectAllCheckColumnHeader'
...
],
* With sortable set to true, a click on the checkbox would (un)check all checkboxes AND sort the grid which is confusing. It's better to set sortable to false.
Upvotes: 1
Reputation: 61
You can take a look at my variant of generating a checkbox in each column header. Check checkcolumn with select all with the discription or just fiddle with example.
My checkcolumn:
Ext.define('Fiddle.CheckColumn', {
extend: 'Ext.grid.column.CheckColumn',
alias: 'widget.fiddlecheckcolumn',
renderTpl: [
'<div id="{id}-titleEl" data-ref="titleEl" {tipMarkup}class="', Ext.baseCSSPrefix, 'column-header-inner<tpl if="!$comp.isContainer"> ', Ext.baseCSSPrefix, 'leaf-column-header</tpl>',
'<tpl if="empty"> ', Ext.baseCSSPrefix, 'column-header-inner-empty</tpl>">',
'<span class="', Ext.baseCSSPrefix, 'column-header-text-container">',
'<span class="', Ext.baseCSSPrefix, 'column-header-text-wrapper">',
'<span id="{id}-textEl" data-ref="textEl" class="', Ext.baseCSSPrefix, 'column-header-text',
'{childElCls}">',
'<img class="', Ext.baseCSSPrefix, 'grid-checkcolumn" src="' + Ext.BLANK_IMAGE_URL + '"/>',
'</span>',
'</span>',
'</span>',
'<tpl if="!menuDisabled">',
'<div id="{id}-triggerEl" data-ref="triggerEl" role="presentation" class="', Ext.baseCSSPrefix, 'column-header-trigger',
'{childElCls}" style="{triggerStyle}"></div>',
'</tpl>',
'</div>',
'{%this.renderContainer(out,values)%}'
],
constructor : function(config) {
var me = this;
Ext.apply(config, {
stopSelection: true,
sortable: false,
draggable: false,
resizable: false,
menuDisabled: true,
hideable: false,
tdCls: 'no-tip',
defaultRenderer: me.defaultRenderer,
checked: false
});
me.callParent([ config ]);
me.on('headerclick', me.onHeaderClick);
me.on('selectall', me.onSelectAll);
},
onHeaderClick: function(headerCt, header, e, el) {
var me = this,
grid = headerCt.grid;
if (!me.checked) {
me.fireEvent('selectall', grid.getStore(), header, true);
header.getEl().down('img').addCls(Ext.baseCSSPrefix + 'grid-checkcolumn-checked');
me.checked = true;
} else {
me.fireEvent('selectall', grid.getStore(), header, false);
header.getEl().down('img').removeCls(Ext.baseCSSPrefix + 'grid-checkcolumn-checked');
me.checked = false;
}
},
onSelectAll: function(store, column, checked) {
var dataIndex = column.dataIndex;
for(var i = 0; i < store.getCount(); i++) {
var record = store.getAt(i);
if (checked) {
record.set(dataIndex, true);
} else {
record.set(dataIndex, false);
}
}
}
});
Upvotes: 6
Reputation: 464
Worked out how to do it; hard code a checkbox with an id into the header text of each check column, move the scope of the grid and store to be initialised as global (but actually constructed on Ext.ready), then have global functions that operate on the datastore records via for loops:
outside of Ext.ready:
var processGrid = null;
var processStore = null;
function headerClick(col, int){
if(document.getElementById(col).checked==true)
{
selectAllInColumn(int, true);
}
else
{
selectAllInColumn(int, false);
}
}
function selectAllInColumn (column, checked, eOpts){
//foreach record in data store
for( var i = 0; i < processStore.getCount(); i++)
{
if(checked)
{
// get record
var record = processStore.getAt(i);
// for current column and each preceding column set process step to true and check the header
for(var j = 1; j <= column; j++) {
document.getElementById('HeaderPhase'+j).checked = true;
record.set('Phase'+j, true);
}
}
else
{
var record = processStore.getAt(i);
for(var j = column; j <= 10; j++) {
document.getElementById('HeaderPhase'+j).checked = false;
record.set('Phase'+j, false);
}
}
}
}
function startCheckHeaderCheckBox(){
// foreach checkcolumn
for(var i = 1; i <= 10; i++)
{
// start running tally per column
var checkedTotal = 0;
// foreach record in data store
for (var j = 0; j < processStore.getCount();j++)
{
var record = processStore.getAt(j);
if (record.get('Phase'+i) == "true"){
checkedTotal++;
}
}
if(checkedTotal==processStore.getCount())
{
document.getElementById('HeaderPhase'+i).checked=true;
}
else
{
document.getElementById('HeaderPhase'+i).checked=false;
}
}
}
function inProgressCheckHeaderCheckBox(columnIndex){
for( var i = 1; i <=columnIndex; i++)
{
var checkedTotal = 0;
for (var j = 0; j < processStore.getCount(); j++)
{
var record = processStore.getAt(j);
if (record.get('Phase'+i)){
checkedTotal++;
}
}
if(checkedTotal==processStore.getCount())
{
document.getElementById('HeaderPhase'+i).checked=true;
}
}
}
Inside Ext.ready:
// before loading the grid.Panel; onCheckChange called
function onCheckChange (column, rowIndex, checked, eOpts) {
var record = processStore.getAt(rowIndex);
var columnIndex = column.getIndex();
for (var i = 1; i <= 10; i++) {
if(checked) {
if (i <= columnIndex) {
record.set('Phase'+i, 'true');
inProgressCheckHeaderCheckBox(columnIndex);
}
}
else {
if (i >= columnIndex) {
record.set('Phase'+i, false);
document.getElementById('HeaderPhase'+i).checked = false;
}
}
}
}
// after loading the grid.Panel to govern setting header check boxes on load
processStore.on('load', startCheckHeaderCheckBox);
// put in each checkColumn, this is the first phase header, change the numbers to match each phase
header: 'Phase 1 <br /> <input type="checkbox" id="HeaderPhase1" style="x-grid-checkcolumn" onclick="headerClick(\'HeaderPhase1\', 1)"/>',
Hope this helps other people, bear in mind my implementation will need to be tailored to whatever dataset you're using, and is dependent on names and IDs being standardised as Phases/HeaderPhases with numbers appended.
Upvotes: 1