Load array in Handsontable cell - javascript

While changing the values in one cell I have to load array of values (as drop down) in other cell.
I don't know how to load array in cell.
What I tried is:
afterChange: function(changes, source) {
if(!changes) {
return;
}
$.each(changes, function(index, element) {
var row = element[0];
var col = element[1];
var oldVal = element[2];
var newVal = element[3];
if(col==6) {
tsContainer.setDataAtCell(row, 1, pjtArray);
}
});
}
Here I used setDataAtCell method, It loads the array as string(comma separated).
And I tried
var arr = [['123'],['dfg'],['678'],['asd']];
tsContainer.populateFromArray(row, 1, arr);
But it load the array each element in each cell.
I have to display all element in one cell as dropdown.
In handsontbale column property we can set array as source. I need something like that.
Since the data is dynamically loaded I have to write this in afterChange event.
Please help me..

Your guess was correct, you need to change the property source of the column to modify the dropdown values.
Let's take the following initial data set :
myData = [
['source1', 'option1'],
['source1', 'option3'],
['source2', 'option5'],
['source2', 'option7']
],
As well as the following dropdown values :
selectSource = ['source1','source2'],
source1 = ['option1','option2','option3','option4'],
source2 = ['option5','option6','option7','option8'],
And let's say you want to modify the dropdown values of column 2 depending on the value of column 1. (If column 1 equal to 'source1', the dropdown values of column 2 will be the array source1, if it's equal 'source2', source of column 2 will be the array source2) :
Get the properties of the cell with getCellMeta(row,column)
Access the source and change it for your new array
Parameter row is the current row where you triggered the event afterChange, value is the value of column 1 for this row:
function setDropDownSource(row, value) {
var instance = this;
var cellPropertiesOption = instance.getCellMeta(row,1); // Column 1 is your 2nd column
if (value == 'source1')
cellPropertiesOption.source = source1;
else if (value == 'source2')
cellPropertiesOption.source = source2;
else
cellPropertiesOption.source = null;
}
This function is called in the afterChange event as follow :
$.each(changes, function (index, element) {
var rowIndex = element[0];
var columnIndex = element[1];
var newValue = element[3];
if (columnIndex == 0) { // Checking if the event has been triggered within the column SourceSelector
setDropDownSource.call(instance, rowIndex, newValue);
instance.render();
}
});
I also add an afterLoadData event to set the source for each rows of the 2nd column when loading the page. But I also wanted to empty the current value of a cell when the source is changed. So you will find an additional parameter for the function to set the condition if the cell needs to be empty or not when changing the source :
if (!init)
instance.setDataAtCell(row,1,"");
You can find the full working example in this JSFiddle.

Related

How to find and select table from Document in Apps Script?

I'm creating a function in Google Apps Script. The purpose of this function is selecting the table from the document and move values to the created Spreadsheet. The problem is that I can't get the table from the document (debugging is OK, but logs show selected table as empty {}).
function addAnswersTable() {
var File = function(Path) { // File object constructor
this.Path = Path;
this.Doc = DocumentApp.openById(this.Path);
this.getTable = new function()
// This function defines
// a getTable method to get
// the first table in the Document
{
if (this.Doc != undefined) {
var range = this.Doc.getBody();
var tables = range.getTables();
var table = tables[0];
return table;
}
}
}
// Creating Excel Table, where first column
// of selected table should be moved
var Table = SpreadsheetApp.create("AnswersTable");
// Creating new File object
var TrueAnswersFile = new File
('1_ne9iBaK-Z36yUYrISr3gru3zw3Qdsneiu14sWnjn34');
// Calling getTable method to get the table placed in File
var TrueAnswersTable = TrueAnswersFile.getTable;
for (var i = 1; i <= TrueAnswersTable.getNumRows; i++) {
// Filling spreadsheet "A" column with cells'
// values ​​from table stored in File
Table.getActiveSheet().getRange("A" + i).setValue(TrueAnswersTable.getCell(1, i).getValue());
};
}
I except the output in Spreadsheet column "A" like :
A1. Just
A2. Cells'
A3. List item with
A4. Values From Table
Actually spreadsheet is empty
You want to retrieve the values from the column "A" of Google Document and put the values to the column "A" of the created Spreadsheet.
The table of index 0 in the Document has 4 rows and 1 column.
The values of each row is Just, Cells', List item with, Values From Table.
I could understand like above. If my understanding is correct, how about this modification?
Modification points:
In your script, the method is not used as the function. By this, the method is not run.
For example, TrueAnswersFile.getTable and TrueAnswersTable.getNumRows.
No method is used.
For example, getValue() of TrueAnswersTable.getCell(1, i).getValue().
new of this.getTable = new function() is not required.
In your script, getCell(1, i) of TrueAnswersTable.getCell(1, i) retrieves the values at from column "B" of the row 2.
If you want to retrieve the values from the row 1 of the column "A", please modify to getCell(i - 1, 0). But in this modification, the start of index is 0. So you can use getCell(i, 0).
When setValue() is used in the for loop, the process cost becomes high. In your case, you can use setValues() instead of it.
When above points are reflected to your script, it becomes as follows.
Modified script:
function addAnswersTable() {
var File = function(Path) {
this.Path = Path;
this.Doc = DocumentApp.openById(this.Path);
this.getTable = function() { // Modified
if (this.Doc != undefined) {
var range = this.Doc.getBody();
var tables = range.getTables();
var table = tables[0];
return table;
}
}
}
var Table = SpreadsheetApp.create("AnswersTable");
var TrueAnswersFile = new File('1_ne9iBaK-Z36yUYrISr3gru3zw3Qdsneiu14sWnjn34');
var TrueAnswersTable = TrueAnswersFile.getTable();
var values = []; // Added
for (var i = 0; i < TrueAnswersTable.getNumRows(); i++) { // Modified
values.push([TrueAnswersTable.getCell(i, 0).getText()]) // Modified
};
Table.getRange("A1:A" + values.length).setValues(values); // Added
}
References:
getCell(rowIndex, cellIndex)
getText()
Benchmark: Reading and Writing Spreadsheet using Google Apps Script

How to update Kendo grid aggregates without refreshing

I am working on a kendo grid with remote data, and currently when updating my grid, I use the dataItem.set() method to manually sync the grid with the data. This works fine, but unfortunately the aggregates in the footer are not refreshed. They refresh only when calling dataSource.fetch(), which hangs up the application for about 10 seconds. The users however, would like more excel-like responsiveness where every change they make is immediately reflected in the total, but also persisted to the database. Is this possible using the kendo api? Or do I have to do this manually with jQuery?
Edit: doesn't look like there's a built-in way so I fixed manually w/ jQuery.
Edit 2: Here's the code I used, generalized a bit and taking out some application specific quirks.
Kendo Grid Configuration:
$(gridId).kendoGrid({
columns: [
{
field: fieldToUpdate,
editor: customEditor,
//add 'data-field' attribute to footer/group footer
footerAttributes: { 'data-field': fieldToUpdate },
groupFooterAttributes: { 'data-field': fieldToUpdate }
},
//other fields...
],
//other config...
});
Custom Editor:
function customEditor(data) {
//store original and new value
//append textbox
//call custom update passing td and data w/ original/new values
}
Find Affected Aggregate Cells:
//Gets all affected aggregate cells after an update
function getTotalsCells($container, updatedField) {
var groups = $('#grid').data('kendoGrid').dataSource.group(),
$totals = $('.k-footer-template>td[data-field="' + updatedField + '"]'),
$row = $container.parent('tr');
for (var i = 0; i < groups.length; i++) {
var $groupTotal = $row.nextAll('.k-group-footer')
.eq(i)
.find('[data-field="..."]');
$totals = $totals.add($groupTotal);
}
return $totals;
}
Update Totals
$.fn.updateTotal = function (delta) {
this.each(function () {
var $container = $(this);
var origTotal = parseFloat($container.text() || 0);
var total = origTotal + delta;
$container.text(total);
});
};
Custom Update:
function updateGrid($container, data) {
var difference, field;
//get difference and updatedField
var $totals = getTotalsCells($container);
$totals.updateTotal(difference);
}
I feel like there must be a better way to do this, but the aggregate model doesn't seem to update.
My solution was to define a function that manually calculates the results and call this from within the footer template. Whenever the grid is refreshed the footer is also updated.
Client Template: #: sumDebits() #
function sumDebits() {
var $grid = $('#GridId');
var kendo = $grid.data().kendoGrid;
var data = kendo.dataSource.data();
var total = 0;
for (var i = 0; i < data.length; i++) {
var debit = parseFloat(data[i].Form.debit);
if (debit == NaN) {
debit = 0;
}
total = total + debit;
}
return total;
}
I had almost similar problem. I had a KendoGrid which i needed to refresh the row only (update HTML and Data) and update the groupFooterTemplateand and the footerTemplate after a modal close (which it had the edit cells i needed to update to the grid). I had "#progress/kendo-ui": "^2019.2.626". I know that is uses set but in this version set updates everything.
Bellow is the Code where you update groupFooterTemplateand ,footerTemplate , Html(row) and also excel is updated withoute Total Refresh of the Grid.
let grid = $('#grid').getKendoGrid(); // Kendo Grid
let dataItem = grid.dataItem(tr); // tr: JQuery (selected Row)
let index= grid.items().index(grid.select()); // or the selected Row
dataItem = data.length > 0 && data.length === 1 ? data : dataItem; // data is the new item with the same properties as the item datasource of the Grid. You can update all properties like below or just set one property.
dataItem.dirty = true;
let rowItem = grid.dataSource.at(index);
for (let [key, value] of Object.entries(dataItem )) {
rowItem.set(key, value); //
}
var grid = $("#gridName").data('kendoGrid');
var aggregateSum = grid.dataSource._aggregateResult;
//below code give you current aggregate value.
var sum = aggregateSum.<your_aggregate_columnname_here>.sum;
//assuming my Grid Column name is 'Amount'
var sum = aggregateSum.Amount.sum;
To change the aggregate value without refreshing page or without fetching the datasource, follow below steps
// Set the current aggregate value to 0
aggregateSum.<your_aggregate_columnname_here>.sum = 0;
// i.e. aggregateSum.Amount.sum = 0;
// Loop trough Grid data row by row
var gridData = grid.dataSource.data();
for (var i = 0; i < gridData.length; i++) {
var dataRow = gridData[i];
// to set the aggregate sum value
aggregateSum.Amount.sum += value;
// to set the cell value for that particular row
dataRow.set(ColumnName, value);
}
Note : Make Sure Call the Set() function in the end after updating the aggregate sum. If you will call Set function before setting aggregate sum, you will not be able to see the last iteration changes, as the changes will only reflect after set() function will get executed

Kendo UI Grid select by data item

I have a Kendo UI Grid with a large datasource and paging.
I have an event that fires where I know the underlying data item that I want to select, but am unsure on how to programatically page/select this item in the grid. If the item is not on the current grid page, I cannot use datasource.view() to poke through when the data is not on the current page.
Does anyone know how I can select an item by its underlying data source object?
I've got a similar situation to where i am at #:
http://jsfiddle.net/Sbb5Z/1050/
I can get the data item with the following:
change: function (e) {
var selectedRows = this.select();
var dataItem = this.dataItem(selectedRows[0]);
}
But then I don't know how to select the same row in the other grid.
Basically in the select event of one grid, I want to go select the same item in another grid. These are not the same datasource, as they have different page setups, but it is the same underlying data array.
I have the data item in the target grid -- but I have no clue how to page/select it in the target grid.
Edit:
The best I've come up with sofar is creating a datasource with the same parameters as the original, and paging through it programatically, until I find what I am looking for. Surely there must be a better way?
I've gotten this back from Telerik, and is a little cleaner:
http://jsfiddle.net/RZwQ2/
function findDataItem(theGrid, dataItem) {
//get grid datasource
var ds = theGrid.dataSource;
var view = kendo.data.Query.process(ds.data(), {
filter: ds.filter(),
sort: ds.sort()
})
.data;
var index = -1;
// find the index of the matching dataItem
for (var x = 0; x < view.length; x++) {
if (view[x].Id == dataItem.Id) {
index = x;
break;
}
}
if (index === -1) {
return;
}
var page = Math.floor(index / theGrid.dataSource.pageSize());
var targetIndex = index - (page * theGrid.dataSource.pageSize()) + 1;
//page is 1-based index
theGrid.dataSource.page(++page);
//grid wants a html element. tr:eq(x) by itself searches in the first grid!
var row = $("#grid2").find("tr:eq(" + targetIndex + ")");
theGrid.select(row);
console.log('Found it at Page: ' + page + 'index: ' + targetIndex);
}
You need to have a common id, or field in the data that you can use to uniquely identify the object in the other dataSource, because the kendo generated UID's are not going to be the same accross two different DataSource instances.
Most generally you define the id in the Model you bound to the grid, which you can use to quickly pluck items from the datasource
change: function (e) {
var selectedRows = this.select();
var dataItem = this.dataItem(selectedRows[0]);
var otherItem = otherGrid.dataSource.get(dataItem.id) // will get
}
if you don't have a common ID field specified in the model, but do know how to find the item you can loop through the data source looking for it
var selectedRows = this.select();
var dataItem = this.dataItem(selectedRows[0]);
var data = otherGrid.dataSource.view();
var otherItem;
for ( var i = 0; i < data.length; i++ ){
if( data[i].myCommonField === dataItem.myCommonField ) {
otherItem = data[i];
break;
}
}
UPDATE:
to select the item in the other grid you need to do this:
var elements = otherGrid.items(),
element;
element = elements.filter("[data-uid='" + otherItem.uid + "']")
otherGrid.select(element) // to select just the one item
//OR
otherGrid.select( otherGrid.select().add(element) ) // to add the item to the current selection
I the fiddle you provided uses a really old version of kendo Grid where this won't work...I just realized. are you stuck on the 2011 version? I can probably get something to work at least in theory but the above will work in the newer versions
essentailly you need to match the item you have to a DOM element, in later versions you can use UID because the dom elements all get that on them "data-uid" it looks like if you at id to your model: { } def you can get the tr elements to have data-id which you can use to select the right select using jquery. I use the items()1 method which also doesn't seem to exist on the early version but you can usegrid2.table.find("tr[data-id=]")` instead I believe
Assume div id will be Grid then first we need find the kendoGrid
var grid = $("#Grid").data("kendoGrid");
then call the grid.select() to select the currently selected one
finally call the grid.dataItem() to get the selected item.
var selectedDataItem = grid.dataItem(grid.select());
To expand upon others, I have a method that takes a single (or multiple) ids to match against:
function selectItems(grid, idAr)
{
if(!idAr instanceof Array)idAr = [idAr];
var items = grid
.items()
.filter(function(i, el)
{
return idAr.indexOf(grid.dataItem(el).Id) !== -1;
});
grid.select(items);
}
* Obviously Id could be replaced by any field that is in your data item.
Use for selection:
selectItems(grid, "5");
selectItems(grid, ["6", "7"]);

Javascript: sorting objects

I'm looking for some ideas of how to accomplish this as I am hitting a wall on it.
I have a table that displays data pulled from a MySQL db. The table goes in a sequence of a row of 13 cells with displayed data followed by a hidden row of one cell. The hidden row is toggled by clicking a link in cell index 1 of the previous row. Like so:
row 1 : click this cell to show row 2 : another cell : another cell : ad nauseum till we get to 13 :
row 2 which is hidden
row 3 : click this cell to show row 2 : another cell : another cell : ad nauseum till we get to 13 :
row 4 which is hidden
...
so using jquery I pulled all the rows, then set a test to determine if it was a displayed row or hidden row, if it was displayed then I put that row and the following one into an object and then placed that object into another object, like so:
//this function is for sorting the data in the table
$(".sort").click(function() {
//get what column we are sorting by
var sortBy = $(this).attr('sortBy');
//set the colnum to sort by
if (sortBy == "itemname") {
var sortCol = 1;
} else if (sortBy == "priority") {
var sortCol = 2;
} else if (sortBy == "livedate") {
var sortCol = 10;
} else if (sortBy == "status") {
var sortCol = 11;
} else if (sortBy == "designer") {
var sortCol = 12;
}
//get the table data
var tableData = getTableData("NO", "null", "YES", sortBy);
//get all the rows
var tableRowArray = $("#productTableBody tr");
//declare new table object
var tableObj = new Object;
var rowPackage = new Object;
//loop through tableRowArray and put rows into packages of two, the main row and the hidden row
for(var t=0; t<tableRowArray.length; t++) {
if($(tableRowArray[t]).children(":first").attr('class') == "colorCode") {
rowPackage[t] = $(tableRowArray[t]).children();
rowPackage[t+1] = $(tableRowArray[t+1]).children();
//dump rows into tableObj
tableObj[t] = rowPackage;
}
//clean out rowPackage
rowPackage = {};
}
var x=-2;
for(thisRow in tableObj) {
x = x+2;
var sortItem = $(tableObj[thisRow][x][sortCol]).html();
//ack! -->getting stumped here
}
});
I've also collected which column the user wants to sort by. I can then find the cell the user wants to sort by. I know I need to pull that info, put into an array and sort but I guess I am getting stumped on how to apply the sorted array back to my tableObj so I can rewrite the table body HTML...the reason I am getting stumped is that some of the data to be sorted will be identical, for example if sorting by designer the data could be this {"a", "b", "c", "c", "a", "a", ""a"}, which when sorted would be a, a, a, a, b, c, c, but since some are identical I can't go back and loop through the object and find the entry that matches the first item in my sorted array, 4 items will match it. So how do I determine which entry in the object matches up with the first a in the sorted list?
Thanks in advance.
That's a tough one, but I suppose there is hardly anything impossible in this life.
I would go like this (using Underscore library)
var packs = [];
// assuming you always have even number of tr's
$("#productTableBody tr:odd").each(function(i, tr){
packs.push( {main: tr, sub: tr.next()} );
// tr.next() will be :even, so it's not yet selected
// now detach rows from the table
// note the order - next() wont work otherwise
tr.next().remove();
tr.remove();
});
var sortedPacks = _(packs).sortBy(function(pack){
return $(pack.main).find('td:nth('+sortCol')').text();
});
// now in sortedPacks you have touples of rows sorted by "main" rows sortCol column
// and you would probably want to restore the table now
$.each(packs, function(i, pack){
$("#productTableBody").append(pack.main).append(pack.sub);
});
The code may not reflect your situation perfectly, but I suppose you should be able to get the main idea.
Not really to easy to get what you are asking for here, but this will at least enable you to get the data sorted.
Start by collecting your data into an array, eg data, each row can be represented by either an array or an object.
Now simply call
data.sort(function(a, b){
// select the two values from a and b depending on the column to sort by
a = a[colNr];
b = b[colNr];
return a == b ? 0 : a < b ? -1 : 1;
});
Now you can easily rebuild your table based on the sorted array.
If you during data-collection also added a reference to the row to the array/object, then you can now remove all rows from the table, loop over the data array, and add each node back to the table.

How do I return a specific cell value in SlickGrid?

For the life of me I can't seem to return a specific cell value in SlickGrid. There is an example on SO here. But this did not work either. I am using the SlickGrid tutorial #7 as my example. All I am trying to do is add a click event to a cell that returns that cells value.
Note: I am loading my data via JSON request and all works well. My click event fires but the value is undefined. For brevity sake I will omit my grid columns and options code. Here is the code and thanks. All and all this is a great grid.
var sortcol = "id";
var sortdir = -1;
$(function () {
$.getJSON(baseURL() + 'programs', function (data) {
dataView = new Slick.Data.DataView();
grid = new Slick.Grid($("#program-grid"), data, programColumns, programOptions);
grid.onSort = function (sortCol, sortAsc) {
sortdir = sortAsc ? 1 : -1;
sortcol = sortCol.field;
if (sortAsc == true) {
data.sort(compare);
}
else {
data.reverse(compare);
}
grid.render();
};
grid.onClick = function (e, row, cell) {
if (programColumns[cell].id == "id") {
var x = data[row][cell].field;//This is where I am stuck
window.location.href = "http://google.com/" + x;
}
}
});
});
You are almost there, you should use data[row].fieldname. Do you have a field in your data called "field"?
If you're using dataView, it's simple:
// Get the object containing row and cell index of a clicked row.
var gridObject = roundGrid.getCellFromEvent(e);
var row = gridObject.row; // get selected row index
var row_values = roundDataView.getItem(row); // get the row object
// Get the actual value.
var cellValue = row_values[roundGrid.getColumns()[gridObject.cell].field];
Bind to the onCellChange event
grid.onCellChange.subscribe( function( e, args ) {
console.log( args.item[ grid.getColumns()[ args.cell ].field ] );
} );
Returns
"{some value}" etc. "This is the newly changed value"
Docs
onCellChange ({ row: number, cell: number, item: any })
Grid Events (onCellChange)

Categories