Prevent Handsontable cells from being selected on column header click - handsontable

In a Handsontable, when a column header is clicked, all cells of that column are selected. Is the a way to prevent this from happening ?
I don't think there's such an option in the documentation. I didn't find where the events are registered on the DOM within the source code of the Handsontable library itself either.
Any hint would be appreciated. Thanks.

It is possible to stop the event from propagating using the beforeOnCellMouseDown hook, which prevents the cells of the header column that was clicked to be selected:
/**
* #param {MouseEvent} event
* #param {WalkontableCellCoords} coords
* #param {Element} element
*/
var handleHotBeforeOnCellMouseDown = function(event, coords, element) {
if (coords.row < 0) {
event.stopImmediatePropagation();
}
};
Handsontable.hooks.add('beforeOnCellMouseDown',
handleHotBeforeOnCellMouseDown, handsontable);
A very special thanks to Gustavo for his help!

I don't think it's possible to prevent that behavior. I haven't found any clue neither in the documentation nor quickly inspecting the source code.
However, you could deselect the selected cells right after they have been selected. Binding a function to handle the cell click event would do the trick. You could do that either by registering the callback when instantiating your handsontable:
$('#my_handsontable').handsontable({
...
afterOnCellMouseDown: function(event, coords){
// 'coords.row < 0' because we only want to handle clicks on the header row
if (coords.row < 0){
$('#my_handsontable').handsontable('deselectCell');
}
},
...
});
Or by means of a hook:
Handsontable.hooks.add('afterOnCellMouseDown', function(event, coords){
if (coords.row < 0){
$('#my_handsontable').handsontable('deselectCell');
}
});
Alternatively, you could edit handsontable source code and comment the piece of code in walkontableConfig that does select the whole column when a header cell is clicked:
var walkontableConfig = {
...
onCellMouseDown: function (event, coords, TD, wt) {
...
// if (coords.row < 0) {
// instance.selectCell(0, coords.col, instance.countRows() - 1, coords.col);
// instance.selection.setSelectedHeaders(false, true);
// }
...
},
...
};

Yes, There is an trick or we can say option to prevent selecting cell on header click. "Just set selectionMode to single single."
Try below code:
document.addEventListener("DOMContentLoaded", function() {
var example1 = document.getElementById('example1');
var selectOption = document.getElementById('selectOption');
var settings1 = {
data: Handsontable.helper.createSpreadsheetData(10, 10),
width: 700,
height: 272,
colWidths: 75,
rowHeights: 20,
rowHeaders: true,
colHeaders: true,
selectionMode: 'single', // 'single', 'range' or 'multiple',
};
var hot1 = new Handsontable(example1, settings1);
});

Related

Change color of series onclick events across multiple highcharts

I'm looking to change the column color and the line color for the same series across multiple charts based on the click of either the legend or the actual column/line.
On the individual charts I have color changing based on clicking/hovering and I can show hide based on the clicking of the first legend. I just can't seem to get the color to work. I'd like to convert the show hide functionality into color change.
Fiddle: http://jsfiddle.net/jlm578/asdgqzgk/4/
This is the chunk of code I'd like to convert or replace:
events: {
legendItemClick: function (event) {
var visibility = this.visible ? 'visible' : 'hidden';
var series = $('#veiOverallAllLine').highcharts().series[this.index];
if (this.visible) series.hide();
else series.show();
return true;
//return false;
}
}
I hope that I understood your question properly :)
Anyway, you can "convert" the event into color change by first preventing the default action to be executed (which is obviously hiding the chart).
This could be done easily using
event.preventDefault() within the event itself.
Than you can use a general function to handle the color change, which takes the series as parameter, find its 'sibling' and than changes the colors:
function updateColor(series){
if(series == undefined) return;
var color = series.color == '#ffffff' ? '#851E20' : '#ffffff';
var sibling = $('#veiOverallAllLine').highcharts().series[series.index];
series.update({color:color});
sibling.update({color:color});
}
(More generalization could be done here but its up to you..)
Than, the whole plotOptions should look like that more or less:
plotOptions: {
series: {
allowPointSelect: true,
states: {
select: {
color: '#851E20'
}
},
events: {
click: function(){
updateColor(this);
},
legendItemClick: function (event) {
event.preventDefault();
updateColor(this);
return true;
}
}
}
},
Here you can see an example: http://jsfiddle.net/a366e89c/2/
NOTE! in the example only the upper chart changes the color of both charts, you just need to copy the lines into the second chart...

Marionette Show new element after increment

I am attempting to have a view where after every fetch of 20 different products from my backend (.fetch()), the DOM will display a product ad. The problem I am running into is that every time the collection fires the add event, the CompositeView is re-rendered.
How can I add Children Views to a CompositeView without re-rendering the whole parent?
define(["marionette", "lodash", "text!fonts/products/template.html",
'fonts/products/item-view', 'fonts/products/model', 'eventer'],
function(Marionette, _, templateHTML, ProductItemView, ProductsModel, eventer) {
'use strict';
var ProductsView = Marionette.CompositeView.extend({
template: _.template(templateHTML),
childView: ProductItemView,
childViewContainer: '.items',
productsLimit: 150,
initialize: function() {
this.listenTo(eventer, 'sort:products', this.sortCollection, this);
this.listenTo(this.collection, 'reset sort add', this.render, this);
this.listenTo(this.collection, 'sync', this.setupSync, this);
},
sortCollection: function(field) {
this.collection.sortByKey(field);
},
setupSync: function() {
this.setupWindowScrollListener();
this.render();
},
productsEnd: function() {
eventer.trigger('products:end');
},
setupWindowScrollListener: function() {
var $window = $(window),
$document = $(document),
that = this,
collectionSize = that.collection.length;
if(collectionSize <= that.productsLimit) {
$window.on('scroll', _.throttle(function() {
var scrollTop = $window.scrollTop(),
wHeight = $window.height(),
dHeight = $document.height(),
margin = 200;
if(scrollTop + wHeight > dHeight - margin) {
eventer.trigger('fetch:more:products');
$window.off('scroll');
}
}, 500));
} else {
that.productsEnd();
}
},
});
return ProductsView;
});
Generate Ad Code
Here is the code I am using to try to generate ads
https://gist.github.com/dennismonsewicz/aecf9bd0befe63e96ee6
What's causing your parent view to re-render, is that you're calling a render on a collection add event :)
this.listenTo(this.collection, 'reset sort add', this.render, this);
Unless you really want to re-render that parent CompositeView you don't need this listener at all because Marionette handles:
collection#add: _onCollectionAdd will render and place new childviews in the default (based on a comparator, if none then id) sort order
collection#reset: Marionette already has a listener on your CompositeView that will call this.render for you if your collection is reset
collection#sort: If you provide your view { sort: true } as an option to your CompositeView, it will handle 'sort' events on your collection. The default is to sort the children views and then call render on the CompositeView. If you don't want your CompositeView to be re-rendered, pass { reorderOnSort: true } and the children view will be efficiently reordered without re-rendering anything (parent nor children), simply moving the children DOM elements in the sort order dictated by the collection.comparator.

Extjs 4 disable sort if grid is empty

Hello stackoverflow community,
is it somehow possible to disable the sorting mechanism in a grid for every column on a condition? Like...
if the grid hasn't any data loaded,
the sorting should be disabled,
else enabled.
I have the problem that if there is no data and you click on a column to sort, the remote sorting mechanism, will start loading the whole data and sorts it, too ...
This behaviour isn't needed or wished, so I want to disable the sorting possibility.
Your help is appreciated.
Thanks in advance and have a nice day.
This is a bit of a hack, but seems to work OK:
http://jsfiddle.net/q43HC/6/
var data = [{
data1: 'test',
data2: 'test'
}, {
data1: 'test2',
data2: 'test2'
}];
var store = Ext.create('Ext.data.Store', {
fields: ['data1', 'data2'],
data: data
});
Ext.define('SampleGrid', {
extend: 'Ext.grid.Panel',
store: store,
height: 250,
width: 400,
title: 'My Window',
columns: [{
text: 'test',
dataIndex: 'data1'
}, {
text: 'test2',
dataIndex: 'data2'
}],
bbar: [{
text: 'Clear Store',
handler: function() {
store.removeAll();
var grid = this.up('grid'),
cols = grid.query('gridcolumn'),
i = 0,
len = cols.length;
for (; i < len; i++) {
cols[i].doSort(null);
cols[i].__toggleSortState = cols[i].toggleSortState;
cols[i].toggleSortState = function() {};
}
}
}, {
text: 'Reload Store',
handler: function() {
store.loadData(data);
var grid = this.up('grid'),
cols = grid.query('gridcolumn'),
i = 0,
len = cols.length;
for (; i < len; i++) {
if (cols[i].__toggleSortState) {
cols[i].toggleSortState = cols[i].__toggleSortState;
}
}
}
}],
renderTo: Ext.getBody()
});
Ext.onReady(function() {
var grd = new SampleGrid();
});
I am just changing the sort state when the data is removed in order to remove any current sorting then replacing the toggleSortState function with an empty one so clicking the header will not sort the column. When reloading the store I put the function back.. You will have to adapt this to your project, but could create a store aware grid with similar logic.
You can do this globally by overriding the Ext.data.Store.sort method. The system I was working on uses remoteSort and hence hits the server everytime the user clicks the column header to sort, even if the grid itself is empty, pretty much same with your problem.
This is the code that you only need to declare in a single location (ie. your ext-overrides file or commons file), instead of in every single grid:
Ext.define('Ext.overrides.data.Store', {
override: 'Ext.data.Store',
sort: function() {
//prevents the grid from submitting a request if store is empty, for remote sort
if (this.count() > 0) {
this.callParent(arguments);
}
}
});
I have written the following function to achieve the same solution:
function disableColumnSorting(disable){
var grid = Ext.getCmp('billRecordGrid'),
cols = grid.query('gridcolumn'),
colLength = cols.length,
i = 0;
for(i; i < colLength; i++) {
if (disable) {
cols[i].sortable= false;
} else {
cols[i].sortable= true;
}
}
}
Thanks to drew630, you gave me a helpful hint, so I could solve my problem on my own.
If you do not want anything to happen when the grid is empty, you could put this in the store of the grid:
listeners: {
beforeload: function() {
return false when my conditions is met.;
}
}
This way your store will not load and nothing will happen. This is also handy when you have a pager component on the grid and where your server expects e.g. extraParams that are not yet set. If you expect extraParams, and when not yet set, the user clicks the refresh on the pager, you end up with exceptions. But with this check, you can prevent a call to the back-end if conditions are not met.
I sometimes set my api.read URL to undefined, (in cases where the store needs extraParams that are not yet set) and only when those params are available, I also set the URL (with a call in the store itself where the url is already set e.g. activateRead(). In the beforeload, I then test if the api.read is undefined or not.
Hope it helps.
Regards
Lawrende

How to set selected extjs combo value to another combobox

I have two Extjs Comboboxes
var cb1 = new Ext.form.ComboBox({
fieldLabel: 'Combobox1',
width: 100,
listeners:{
scope: this,
'select': this.reset
}
});
var cb2 = new Ext.form.ComboBox({
fieldLabel: 'Combobox2',
width: 100,
baseParams: { loadInitial: true, cb1Val: this.cb1.getValue() } // Here I want to get selected value of cb1
listeners:{
scope: this,
'select': this.reset
}
});
I am using this.cb1.getValue() to get selected value of cb1 but I am getting blank value here.
Can anyone tell me what I am doing wrong here.
I think combo 2 is not able to get value of combo 1 as its not loaded while you are calling its value in baseParams. I would recommend this approach in your case -
var cb1 = new Ext.form.ComboBox({
fieldLabel: 'Combobox1',
width: 100,
**id : "combo_1",** // ID property added
listeners:{
scope: this,
'select': this.reset
} });
// Previous code
...
listeners : {
select : function(combo, rec, rind) {
var combo2_val = Ext.getCmp("id_of_combo_1").getValue();
}
Here, on the select renderer of second combo, the value of first should be set in combo2_val. To be more precise, you can also set default value for combo 1. Instead of using ID, you can directly use variable cb1 directly.
Hope this should give you a way.

jqGrid: is it possible to selectively make cells editable?

how can I make one single cell editable out of a not editable column?
my javaScript looks like this:
$( '#grid' ).jqGrid({
// ...
cellEdit : true,
colModel : [
{ name : "id", index : "id", editable : false },
{ name : "wbs", index : "wbs", editable : false },
{ name : "value", index : "value", editable : false }
],
loadComplete : function(data) {
// ... foreach ( cell in data.rows.columns ) ...
if ( cell.shouldBeEditable ) {
jQuery('#grid').setCell(cell.row, cell.col, '', 'green', { editable : true });
}
}
// ...
}
so, after globally setting columns as not editable, I try to set them as editable locally, based on some criteria (to identify them more easily I also paint them green).
Alas, it's not working: cells become green, but when I try to click them they do not become editable.
Inspecting the selected cell with firebug reveals the edit-cell class to be correctly applied.
As a last note, it's working if I set columns as editable in the first instance.
I suggest you do it in reverse. Make the column editable, but disable the cells that you do not want to be editable. This is a function I wrote to disable cells:
// cellName is the name defined in your colModel
function disableGridCell(cellName) {
var cell = $('[name="' + cellName + '"]');
cell.css('display', 'none');
var div = $("<div>")
.css('width', '100%')
.css('height', '100%')
.css('border', '1px solid #000')
.css('background-color', '#CCC')
.text('xxxxxxxxxxxx');
cell.parent().append(div);
}
I call disableGridCell inside of my onEditFunc of my grid's editRow function:
$('#grid').jqGrid('editRow', id, keys, onEditFunc);
function onEditFunc(id) {
if (condition to disable cell) {
disableGridCell('CellName');
}
}
I found a nice workaround to the problem (thanks to Walter for getting me on the right track).
Instead of locking all the cells and selectively unlocking them in a declarative manner (which may lead to long load times), I'm declaring all the cells as editable.
Then I'm attaching a callback to afterEditCell option:
var $grid = $('#grid');
$grid.jqGrid({
// ...
afterEditCell : function(rowid, cellname, value, iRow, iCol) {
if ( shouldNotBeEditable(iRow, iCol) ) {
$grid.setCell(rowid, cellname, '', 'not-editable-cell');
$grid.restoreCell(iRow, iCol);
}
},
// ...
});
This way the cell is immediately reset to its previous value and I ensure that the callback gets called only once per not-to-be-edited cell, since the cell is made into a not-editable-cell after the first call.
colModel:[
{name:'description',index:'description', width:4, sortable: false, editable: true},
{name:'qty',index:'qty', width:1,sortable: false, editable: true},
{name:'cost',index:'cost', width:1, editable: true, sortable: false},
{name:'totalCost',index:'totalCost', width:1, sortable: false}
onCellSelect: function(rowid, iCol, cellcontent, e) {
//here it will allow editing of either qty or cost col for that rowid
//only if both cols have a numeric value already
if(iCol===1 || iCol===2) {
var rowdata = $("#projectTable").getRowData(rowid);
var itemCost = parseInt(rowdata['cost']);
var qty = parseInt(rowdata['qty']);
if(!$.isNumeric(itemCost) || !$.isNumeric(qty)) {
$("#projectTable").jqGrid('setColProp','qty',{editable: false});
$("#projectTable").jqGrid('setColProp','cost',{editable: false});
} else {
$("#projectTable").jqGrid('setColProp','qty',{editable: true});
$("#projectTable").jqGrid('setColProp','cost',{editable: true});
}
}
},

Resources