Google charts column filtering - debugging

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/.

Related

Automatically create a new sheet with data of a row in the mastersheet

Consider I have a mastersheet with names in column A and a link in colum B, starting in row 2.
The code below, for a reason I don't understand, creates only one new sheet with the name in row 2, and sets the value of the row 3 in the newly created sheet.
I'd like that to work as followed :
create a sheet with the name in col A
set values of col A and col B in the newly created sheet
Can someone help me figure out what's going on ?
I read the documentation about loops and getRange but I can't figure it out..
Thank you very much
function createNewSheets() {
var dataSheet = SpreadsheetApp.getActiveSpreadsheet();
var values = dataSheet.getSheetByName('mastersheet').getRange('A2:B').getValues();
// Get sheet with current swimmer name:
var sheet = dataSheet.getSheetByName(swimmerName);
var col = values[i]; // Current row
var swimmerName = col[0]; // swimmer name
// Iterate through all rows of data from "mastersheet":
for (var i = 1; i < values.length; i++) {
// Check if sheet with current swimmer name exists. If it doesn't it creates it:
if (!sheet) {
sheet = dataSheet.insertSheet(swimmerName);
}
// Sets current row of data to the new sheet:
sheet.getRange(1, 1, col.length, col[0].length).setValues(col)
}
}
I believe your goal as follows.
You want to retrieve the values from the columns "A" and "B" from "mastersheet" sheet.
You want to use the values of column "A" retrieved from "mastersheet" sheet as the sheet names.
When the sheet of the retrieved sheet name is not existing in the active Spreadsheet, you want to insert new sheet with the sheet name.
When the new sheet is inserted, you want to copy the same row of the sheet name to the inserted sheet.
You want to achieve this using Google Apps Script.
Modification points:
In your script,
At var sheet = dataSheet.getSheetByName(swimmerName), swimmerName is not declaread.
When swimmerName is not declared elsewhere, an error occurs at this line.
At var col = values[i], i is not declared.
In the for loop,
the 1st index is 1. From getRange('A2:B'), in this case, the values are retrieved from 3rd row.
swimmerName and col are not changed.
In order to achieve your goal, I would like to propose the following flow.
Retrieve the current sheet names.
Retrieve the values from "mastersheet" sheet.
Using the retrieved sheet names and values, the new sheets are inserted and the values are put.
When above flow is reflected to a script, it becomes as follows.
Sample script:
function createNewSheets() {
// 1. Retrieve the current sheet names.
var dataSheet = SpreadsheetApp.getActiveSpreadsheet();
var sheetNames = dataSheet.getSheets().map(s => s.getSheetName());
// 2. Retrieve the values from "mastersheet" sheet.
var masterSheet = dataSheet.getSheetByName('mastersheet');
var values = masterSheet.getRange('A2:B' + masterSheet.getLastRow()).getValues();
// 3. Using the retrieved sheet names and values, the new sheets are inserted and the values are put.
values.forEach(r => {
if (!sheetNames.includes(r[0])) {
dataSheet.insertSheet(r[0]).appendRow(r);
sheetNames.push(r[0]);
}
});
}
In this sample script, sheetNames is growing in the loop and that is used for checking the duplicate sheet names.
References:
getSheets()
insertSheet(sheetName)
appendRow(rowContents)
forEach()

Oracle APEX Interactive Grid: Is there a way to get the corresponding column name, when clicking in a data cell in the Interactive Grid

I have a Dynamic Action which should get the data from the model, depending on the clicked column. Let 's say I have two columns in the interactive grid, column A and B. Depending on the column I clicked in, the DA should be executed and execute a query with the value from column A or B.
The DA is actived on doubleclick and I have the following source to get the value from the IG model.
var regionStaticId = $(this.triggeringElement).closest("div.js-apex-region").attr('id');
var grid = apex.region( regionStaticId ).widget().interactiveGrid("getViews", "grid");
var model = grid.model;
var record = grid.getSelectedRecords()[0];
var value;
// Code to find the the clicked column comes here
if (record) {
value = model.getValue(record, columnName);
}
Now, what I can do is add an extra css class to the particular cells, with the name of the source column. But that would be like hardcoding in my opinion. Like this.
if ($(this.triggeringElement).hasClass('columnA')) {
columnName = 'COLUMN_A';
}
else if ($(this.triggeringElement).hasClass('columnB')) {
columnName = 'COLUMN_B';
}
Is there a way to determine the clicked column, based on the triggering element?
Help is very much appreciated.

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.

GetColumn Information and Width on Resize - Slick Grid

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

Resources