GetColumn Information and Width on Resize - Slick Grid - slickgrid

Am working on slick grid where am trying to get the column information like id,name and the new width of column after resize.
I wrote an event which will be triggered when user resizes the column.
grid.onColumnsResized.subscribe(function (e, args) {
//To Do
});
grid.getColumns() will help but how do i identify which column user has resized. is there a way I can get the column index of the resized column?
some start up code from here will save lot of my time
Thanks

The onColumnsResized event triggered by SlickGrid doesn't include any references to the columns which changed.
It's important to note that the width of multiple columns may have changed when this event triggers. Examples of this are:
Using the grid option forceFitColumns: true to force the columns to fit in the width of the grid
Resizing a column so small it affects the columns to its left
Two possible options for implementing this are:
Check columns after change
SlickGrid stores the previous column widths in the property named previousWidth on each column. You can compare the prevoiusWidth and width values to determine which columns changed.
grid.onColumnsResized.subscribe(function (e, args) {
//Loop through columns
for(var i = 0, totI = grid.getColumns().length; i < totI; i++){
var column = grid.getColumns()[i];
//Check if column width has changed
if (column.width != column.previousWidth){
//Found a changed column - there may be multiple so all columns must be checked
console.log('Changed column index : ' + i);
console.log(column);
}
}
});
SlickGrid resets the previousWidth values for all columns whenever a column starts being resized.
You can view an example of this approach at http://plnkr.co/edit/W42pBa2ktWKGtqNtQzii?p=preview.
Modifying SlickGrid
If you are hosting SlickGrid and are comfortable maintaining your own version then you could modify it to include column information in the args of the onColumnsResized event.
In slick.grid.js at line 860 amend the code where the event is triggered to include an array containing the indexes of changed columns. You can also include the index of the column which the user resized if this is useful. The below adds properties named changedColumnIndexes and triggeredByColumnIndex which are passed in the args of the triggered event. I've wrapped the changes for this in comments prefixed //MODIFICATION.
.bind("dragend", function (e, dd) {
var newWidth;
//MODIFICATION - Add array to capture changed column indexes and variable to capture
// the index of the column which triggered the change
var changedColumnIndexes = [];
var triggeredByColumnIndex = getColumnIndex($(this).parent()[0].id.replace(uid, ""));
//MODIFICATION END
$(this).parent().removeClass("slick-header-column-active");
for (j = 0; j < columnElements.length; j++) {
c = columns[j];
newWidth = $(columnElements[j]).outerWidth();
//MODIFICATION - Add column index to array if changed
if (c.previousWidth !== newWidth) {
changedColumnIndexes.push(j);
}
//MODIFICATION END
if (c.previousWidth !== newWidth && c.rerenderOnResize) {
invalidateAllRows();
}
}
updateCanvasWidth(true);
render();
//MODIFICATION - Amend trigger for event to include array and triggeredBy column
trigger(self.onColumnsResized, {changedColumnIndexes: changedColumnIndexes, triggeredByColumnIndex: triggeredByColumnIndex});
//MODIFICATION END
});
In your own code subscribe to the onColumnsResized event and pickup the changed column index from the args of the event.
grid.onColumnsResized.subscribe(function(e, args) {
//Triggered by column is passed in args.triggeredByColumnIndex
console.log('Change triggered by column in index : ' + args.triggeredByColumnIndex);
console.log(grid.getColumns()[args.triggeredByColumnIndex]);
//Column array is passed in args.changedColumnIndexes
console.log('Changed columns are...');
console.log(args.changedColumnIndexes);
//Loop through changed columns if necessary
for (var i = 0, totI = args.changedColumnIndexes.length; i < totI; i++){
console.log(grid.getColumns()[args.changedColumnIndexes[i]]);
}
});
You can view an example of this approach at http://plnkr.co/edit/4K6wRtTqSo12SE6WdKFk?p=preview.

Determining column changes by column.width != column.previousWidth wasn't working for me because sometimes the original and new width were different by an insignificant size (e.g. 125.001 and 125).
I used Chris C's logic and made a PR on the 6pac/SlickGrid project. Here's the commit:
https://github.com/6pac/SlickGrid/commit/ba525c8c50baf18d90c7db9eaa3f972b040e0a6e

Related

Multiple dependent dynamic dropdowns with repeating column dropdowns in Google Sheets

The Google Sheet I have uses code made by user Max Makhrov, code here, to make multiple dependent dynamic dropdowns in columns D-F (for location) and columns H-L (for objectives & activities) in my sample sheet here.
I would like help to modify the script to do two things:
Whatever activity is selected from the dropdown menu in Column I, I would like the same dropdown menu options to be available (to repeat) for columns J-L. As you can see I found a way to do it, but to me it seems clunky and not ideal, and leaves too much room for errors. Users should not select the activity twice, but I've put conditional formatting in to flag that if they do. However:
Ideally, but less importantly, if the dropdown menu items could still repeat for columns J-L but once an activity is selected in previous cells, that option is removed from each of the following repeated dropdown menus in additional columns, up to and including column L. This would help avoid accidentally repeating an activity.
NB: Reference question "How do you do dynamic / dependent drop downs in Google Sheets?"
Thank You!
When one of the drop-down cells is edited you can use an onEdit trigger [1] to iterate through the 4 columns (I-L) and update the drop-downs in each cell removing the option selected in the edited cell. You also need to add the old selected value (previously deleted from other options) to the other drop-downs. For this, you can use getDataValidation [2] and getCriteriaValues [3] functions chained to a Range object to retrieve the current drop-down values array on that range and delete the option matching with the selected option.
Use newDataValidation() [4] function to create a new rule using your updated drop-down values array and setDataValidation [5] function to set the rule to the range.
function onEdit(event) {
var range = event.range;
var sheetName = range.getSheet().getSheetName();
var col = range.getColumn();
var newValue = event.value;
var oldValue = event.oldValue;
//If the edited range is in sheet '3W' and beetween columns I-L
if(sheetName == '3W') {
if(col>=9 && col<=12) {
for(var i=9; i<13; i++) {
//Don't change anything for edited cell
if(col == i) { continue; }
else {
//Get range to update and current dropdown values for that range
var rangeToUpdate = range.getSheet().getRange(range.getRow(), i, 1, 1);
var dropdownValues = rangeToUpdate.getDataValidation().getCriteriaValues()[0];
//Find new edited value and delete it from options array
var index = dropdownValues.indexOf(newValue);
if (index > -1) {
dropdownValues.splice(index, 1);
}
//If previous selected value is not beetween the options, add it
if(oldValue && dropdownValues.indexOf(oldValue) == -1) {
Logger.log(oldValue)
dropdownValues.push(oldValue);
}
//Set new dropdown values to range
var updatedRule = SpreadsheetApp.newDataValidation().requireValueInList(dropdownValues, true).setAllowInvalid(false);
rangeToUpdate.setDataValidation(updatedRule);
}
}
}
}
}
Run just the first time to set all the drop-downs in columns I-L, which are get it from range E1:E10:
function setDropdownsInitially() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
//Range with the dropdown values
var sheet = ss.getSheetByName("indicators");
var dropdownValues = sheet.getRange("E1:E10").getValues();
//Data validation rule
var rule = SpreadsheetApp.newDataValidation().requireValueInList(dropdownValues, true).setAllowInvalid(false);
//Range where the dropdowns will be created
var targetSheet = ss.getSheetByName("3W");
var cells = targetSheet.getRange("I2:L");
//Set data validation rule
cells.setDataValidation(rule);
}
[1] https://developers.google.com/apps-script/guides/triggers/events#google_sheets_events
[2] https://developers.google.com/apps-script/reference/spreadsheet/range#getdatavalidation
[3] https://developers.google.com/apps-script/reference/spreadsheet/data-validation-builder.html#getcriteriavalues
[4] https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app#newdatavalidation
[5] https://developers.google.com/apps-script/reference/spreadsheet/range#setdatavalidationrule

Iterate through jqGrid update every row for a column

I wish to iterate through jqGrid, and for a given column (ie: the second) I wish to insert a value. How do you find the first data row? The documentation warns to not use getRowData if updating row or cell data.
This is what I'm using, but it seems clumsy:
function loadCompleted() {
var $grid = jQuery("#jqGrid"), rows = $grid[0].rows;
for (var i = 1; i < rows.length; i++) {
var row = rows[i];
var id = row.cells[0].innerHTML;
$(row.cells[1]).html("<a href='#' onclick='deleteApp(" + id + "); return false;'>Delete</a>");
}
}
... this works, but it makes the assumption that the first data row is the second row in table #jqGrid. It also relies on index values for columns 1 and 2.
Is there any way to use setRowData when the documentation warns to not use getRowData when editing the row or cells?
The first row in grid is hidden and is used in the jqGrid for internal purposes.
I think that using a custom formatter will do the job.
Example of custom formatter can be found here. If you use Guriddo jqGid you may look into the docs for the parameters passed.

Prevent resizing first column in ListCtrl (ReportView)

I come from this
LVCFMT_FIXED_WIDTH
This format style works, but only after the first column. The first column items is always moveable. Why? Do I have missing something?
for(int i = 0; i<NUM_COLUMNS; i++)
{
m_ListCtrl.InsertColumn(i,_gszColumnLabel[i], _gnColumnFmt[i] | LVCFMT_FIXED_WIDTH, _gnColumnWidth[i], -1);
}
The first column is special for some reason:
If a column is added to a list-view control with index 0 (the leftmost column), it is always LVCFMT_LEFT. Setting other flags on column 0 does not override that alignment. Therefore if you keep inserting columns with index 0, the text in all columns are left-aligned. If you want the first column to be right-aligned or centered you can make a dummy column, then insert one or more columns with index 1 or higher and specify the alignment you require. Finally delete the dummy column.
Perhaps the dummy column trick will work in your case as well?
A problem with LVCFMT_FIXED_WIDTH is that it is not possible to change the column width programmatically either (using SetColumnWidth). If you want to set the column width after the header control construction (for example in OnSize) you have to temporarily remove the LVCFMT_FIXED_WIDTH flag.
Here is an implementation that both has a workaround for not being able to modify the first column and one for not being able to programmatically modify a column with LVCFMT_FIXED_WIDTH:
void
MyTreeeCtrl::SetFixedColumnWidth(int col, int width)
{
int colToModify = col;
// Workaround 1: Insert a dummy column to be able to modify the attributes
// of the first column.
if (col == 0)
{
InsertColumn(0, L"");
++colToModify;
}
// Workaround 2: Temporarily remove the fixed width attribute to be able to
// modify the width.
LVCOLUMN colInfo;
colInfo.mask = LVCF_FMT;
GetColumn(colToModify, &colInfo);
colInfo.fmt = colInfo.fmt & ~LVCFMT_FIXED_WIDTH;
SetColumn(colToModify, &colInfo);
// Set the column width
SetColumnWidth(colToModify, width);
// Restore the fixed width attribute
colInfo.fmt = colInfo.fmt | LVCFMT_FIXED_WIDTH;
SetColumn(colToModify, &colInfo);
// Remove the dummy column
if (col == 0)
{
DeleteColumn(0);
}
}

Google charts column filtering

I am trying to filter a Google line chart columns and using the code shared here in Google Charts-Code for Category Filter
It all works well however I have a number of columns and would like to have the chart start with just one column displayed and allow the user to add in any of the additional columns as needed.
What I've found is that if I play with the initState variable to set it to the one column I want to display initially, it will have that column shown in the selector section but still displays all the columns initially until I select an additional column when it hides the rest and just displays the two I have selected.
So then I tried turning off this part of the code:
// put the columns into this data table (skip column 0)<br>
for (var i = 1; i < data.getNumberOfColumns(); i++) {
columnsTable.addRow([i, data.getColumnLabel(i)]);
initState.selectedValues.push(data.getColumnLabel(i));
}
and replacing it with
columnsTable.addRow([1, data.getColumnLabel(16)]);
initState.selectedValues.push(data.getColumnLabel(16));
which sets the column i'm after (column 16) as the selected column in the selection list but removes the other columns from the list of available columns and still displays all 16 columns.
How can I set this up so it displays the single selected column's data initially yet still gives the ability to pick other columns from the selector?
You want to keep the columnstable.addRow call inside the for loop, as it populates the DataTable used to provide the list of columns. You can set the selectedValue variable as you have it:
// put the columns into this data table (skip column 0)<br>
for (var i = 1; i < data.getNumberOfColumns(); i++) {
columnsTable.addRow([i, data.getColumnLabel(i)]);
}
initState.selectedValues.push(data.getColumnLabel(16));
In order to make the chart draw properly with the initial selection, we need to make a small adjustment to the structure, putting all of the updating code into its own function, and then calling that as necessary:
function setChartView () {
var state = columnFilter.getState();
var row;
var view = {
columns: [0]
};
for (var i = 0; i < state.selectedValues.length; i++) {
row = columnsTable.getFilteredRows([{column: 1, value: state.selectedValues[i]}])[0];
view.columns.push(columnsTable.getValue(row, 0));
}
// sort the indices into their original order
view.columns.sort(function (a, b) {
return (a - b);
});
chart.setView(view);
chart.draw();
}
google.visualization.events.addListener(columnFilter, 'statechange', setChartView);
setChartView();
Here's a working example: http://jsfiddle.net/asgallant/WaUu2/157/.

jqGrid - Issue with editrules when dynamically changing editable property on column model

I am using inline row editing for a jqGrid.
I loop through each row and before calling editRow(), I set the editable property on the column model for certain rows as FALSE (making the column for some rows as non editable). This all works fine until I add editrules to the column model. I get a javascript error "a is undefined" when saving the rows.
Question - Is there a way to make a column non-editable for certain rows and for other rows have an edit rule defined?
Any inputs is greatly appreciated!
I was able to solve this by setting/resetting the editrules property on the colModel before calling saveRow() on each row. Below is the code snippet
function updateEditRuleProp() {
var qtyRule = {required:true, number:true, minValue:1};
if (condition to disable editrules) {
qtyRule = null;
}
jQuery("#tableId").jqGrid('getColProp', 'yourColumName').editrules = qtyRule;
}
Here is the code that calls the above function
var $this = jQuery("#tableId"), ids = $this.jqGrid('getDataIDs'), i, l = ids.length;
for (i = 0; i < l; i++) {
updateEditRuleProp();
jQuery("#tableId").jqGrid('saveRow', ids[i]);
}

Resources