Let's say I have few rows of data populated with numbers. I want to select multiple cells and then on click on a button outside the grid change their values to some other number, let's say '8'. See the sample.
The guys at Telerik gave me this solution:
$(".change").click(function () {
var grid = $("#Grid").data("kendoGrid");
var cellsToChange = grid.select();
for (var i = 0; i < cellsToChange.length; i++) {
var item = grid.dataItem($(cellsToChange[i]).closest("tr"));
item.ProductName = "new value";
}
grid.refresh();
});
But the problem is that I don't know which cells will be selected, so I can't work with item.ProductName, for example. Is there a way to set the value of all selected cells directly, something like cellsToChange[i].value?
You can either get the column name from grid.columns or from the corresponding th element. use the grid.cellIndex method to select the correct column:
$("#change").click(function() {
var selected = grid.select();
var header = grid.thead;
for (var i = 0, max = selected.length ; i < max ; i++) {
var index = grid.cellIndex(selected[i]);
var th = $(header).find("th").eq(index);
// could also use grid.columns[index].field
// (not sure if this gets reordered on column reorder though)
var field = $(th).data("field");
var item = grid.dataItem($(selected[i]).closest("tr"));
item[field] = "new value";
}
grid.refresh();
});
Regarding your comment:
dataItem.set() causes the <tr> elements to get removed from their context (because grid.refresh() will create new rows for the view), and because of that, grid.dataItem() won't give you the expected result with the old DOM elements you still have a reference to.
If you want to use dataItem.set(), you can try something like this as a work-around:
$("#change").click(function () {
var selected = grid.select(),
header = grid.thead,
dataItem,
index,
field,
value,
th;
for (var i = 0, max = selected.length; i < max; i++) {
dataItem = grid.dataItem($(selected[i]).closest("tr"));
index = $(selected[i]).index();
th = $(header).find("th").eq(index);
field = $(th).data("field");
value = "new value " + i;
setTimeout(function (dataItem, field, value) {
return function () {
dataItem.set(field, value);
}
}(dataItem, field, value), 5);
}
});
(demo)
You have to retrieve the ColumnList of your grid first and then loop through it
$(".change").click(function () {
var grid = $("#Grid").data("kendoGrid");
var columnsListOfView = grid.columns;
var cellsToChange = grid.select();
for (var j = 0; i < cellsToChange.length; i++) {
var item = grid.dataItem($(cellsToChange[i]).closest("tr"));
for (var j = 0; j < columnsListOfView.length; j++) {
//Here columnsListOfView[j].field will give you the different names that you need
var field=columnsListOfView[j].field;
item[field] = "new value";
}
}
grid.refresh();
});
Related
So my aim it to loop through all selected item in my kendo grid, but after the first iteration the dataItem method returns undefined.
function myFunction() {
var selectedItem = $("#DropDown").val();
var grid = $("#Grid").getKendoGrid();
var selectedItems = grid.select();
for (var i = 0; i < selectedItems.length; i++) {
var dataItem = grid.dataItem(selectedItems[i]);
if (dataItem != undefined)
dataItem.set("Item", SelectedItem);
}
}
Does anyone know why this might be happening?
That happens because set() performs a grid refresh behind-the-scenes so the DOM is recreated. The array you had with the selected items is lost. You can't rely on the tr's references. As a suggestion I think you can use they indexes instead:
function myFunction() {
var selectedItem = $("#DropDown").val();
var grid = $("#Grid").getKendoGrid();
var selectedItems = grid.select().toArray().map((item) => { return $(item).index(); });
for (var i = 0; i < selectedItems.length; i++) {
var currentItem = grid.tbody.find(`tr:eq(${selectedItems[i]})`);
var dataItem = grid.dataItem(currentItem );
if (dataItem != undefined)
dataItem.set("Item", SelectedItem);
}
}
var selectedItems = grid.select().toArray().map((item) => { return $(item).index(); });
This line gets an array of indexes from the selected grid rows to iterate further ahead;
var currentItem = grid.tbody.find(`tr:eq(${selectedItems[i]})`);
This line retrieves the selected row from by the index.
Demo
I created 3 objects that nest arrays of one another - we'll call them Table, Row and Column so I can show you what's wrong. Table has an array of Rows, and Row has an array of Columns. When I call properties of the Rows from Table, no problem. When I call properties of the Columns from Row, it says undefined, but in the debugger and the console it recognizes the object and it's properties. Maybe I've been staring at it too long but I can't see a fundamental difference.
I stripped the Table layer to be sure it wasn't an issue with nested objects. Here's the code, not working:
function Column()
{
this.sampleProp = "testprop";
this.content = "<td>sometext</td>";
}
function Row(columns)
{
this.columns = [];
this.columns = columns;
this.outputRows = function()
{
var temp = "<tr>";
for(var i = 0; i < this.columns.length; i++)
{
//this is the line that doesn't work and comes out as undefined:
temp += this.columns[i].content;
console.log("Problem: " + this.columns[i].content);
//yet the object exists, and has the correct properties:
console.log(this.columns[i]);
}
temp += "</tr>";
return temp;
};
}
function test()
{
var col = new Column();
console.log("Printing out the value from here works fine: " + col.content);
var cols = [col];
console.log("It's also fine when it's called from an array: " + cols[0].content);
var row = new Row([cols]);
console.log(row.outputRows());
}
Here is the interaction between the parent layer and the rows, working fine:
function Table(rows)
{
this.rows = [];
this.rows = rows;
this.outputTable = function()
{
var temp = "<table>";
for(var i = 0; i < this.rows.length; i++)
{
temp += this.rows[i].outputRows();
}
temp += "</table>";
return temp;
};
}
and the updated test function:
function test()
{
var column = new Column();
var cols = [column];
var row = new Row([cols]);
console.log(row.outputRows());
var rs = [row, row];
var table = new Table(rs);
console.log(table.outputTable());
}
Two rows print out as expected this way, with undefined inside each. I originally had column.content written as a function, it doesn't make a difference.
Please tell me what stupid mistake I'm missing here!
Change that line :
var row = new Row([cols])
into
var row = new Row(cols)
since cols is already an array, you don't need to put it in an array again.
function Column() {
this.sampleProp = "testprop";
this.content = "<td>sometext</td>";
}
function Row(columns) {
// removed this.columns = [] since you are assigning it in the next line
this.columns = columns;
this.outputRows = function() {
var temp = "<tr>";
for (var i = 0; i < this.columns.length; i++) {
//this is the line that doesn't work and comes out as undefined:
temp += this.columns[i].content;
}
temp += "</tr>";
return temp;
};
}
function test() {
var col = new Column()
console.log("Printing out the value from here works fine: " + col.content);
var cols = [col];
console.log("It's also fine when it's called from an array: " + cols[0].content);
var row = new Row(cols); // the problem was here
console.log(row.outputRows());
}
test()
Hi I have added some items to asp:DropDownList from javascript. Like this
if (document.getElementById("<%=gdview.ClientID %>")!=null)
{
var rows = document.getElementById("<%=gdview.ClientID %>").getElementsByTagName('tr');
var cells = rows[1].getElementsByTagName('td');
//alert(cells)
i = 2;
while (i < cells.length)
{
document.getElementById("<%=ddl_noofCols.ClientID %>").options[i] = new Option(i + 1, i);
i++;
}
document.getElementById("<%=ddl_noofCols.ClientID %>").options[2].selected =true;
alert(document.getElementById("<%=ddl_noofCols.ClientID %>").options[2].text);
}
here gdview is gridview. no of columns of gridview are added to dropdownlist
default is options[2] is selected. I cannot the get selecteditem/selectedvalue using ddl_noofCols.SelectedValue which returns null.
How can I get the selectedvalue.
Thanks in advance.
You may want something like:
var table = document.getElementById("<%=gdview.ClientID %>");
var select = document.getElementById("<%=ddl_noofCols.ClientID %>");
var cells;
if (table) {
cells = table.rows[1].cells;
for (var i=2, iLen=cells.length; i<iLen; i++) {
select.options[i] = new Option(i + 1, i);
}
select.options[2].selected = true;
alert(select.options[2].text);
// To get the current value of the select,
// use the value property:
alert(select.value);
}
The value of the currently selected option is available as select.value. If the selected option has no value attribute or property, then the value of the text property is returned except in IE 8 and lower, where you must use something like:
var value = select.value;
if (!value) {
value = select.options[select.selectedIndex].text;
}
I have to change a cell value by code and keep it editable. With this code, I can change the value of the other cell, but the cell 'paidHour' change to a non-editable state.
How to make it editable again?
editoptions: { dataEvents: [{ type: 'keyup', fn: function (e) {
var rowId = $(e.target).closest("tr.jqgrow").attr("id");
var total = parseInt(e.target.value, 10);
var paidWeek = parseInt($("#List").getCell(rowId, 'paidWeek'), 10);
var addHourBank = 0;
if (total >= paidWeek) {
addHourBank += (total - paidWeek);
total = paidWeek;
}
$("#List").setCell(rowId, 'paidHour', total);
I am not sure that I understand you correct. It's important to distinguish editing and editable cells/rows. The editable is the row or the cell which can be edited with respect of form editing, inline editing or cell editing. Probably the cell are editing currently and you want to change the value of the cell. In the case you can't use setCell method. Instead of that you have to get the <td> as DOM or as jQuery wrapper and then set value of the child <input> element of the cell.
For example you can use getColumnIndexByName function defined as following
var getColumnIndexByName = function (grid, columnName) {
var cm = grid.jqGrid('getGridParam', 'colModel'), l = cm.length, i;
for (i = 0; i < l; i++) {
if (cm[i].name === columnName) {
return i; // return the index
}
}
return -1;
}
It gets you the index of the column inside of the row. So you can split the line
var rowId = $(e.target).closest("tr.jqgrow").attr("id");
into
var $tr = $(e.target).closest("tr.jqgrow"), rowId = $tr.attr("id");
The $tr represents the jQuery wrapper of the DOM of <tr>. So to get the <td> cell for 'paidHour' you can use
var iCol = getColumnIndexByName ($(this), 'paidHour');
var $td = $tr.find(">td:nth-child(" + (iCol + 1) + ")");
and to change the value of the cell you can use
$td.find("input").val(total);
I am using the following method to read header names in a table and put in excel. Could anyone let me know how to modify this to support multiple tables with header info and data.
i.e. how to modify to pass table id. "headers" is the id for "th" tag in code.
function write_headers_to_excel()
{
str="";
var myTableHead = document.getElementById('headers');
var rowCount = myTableHead.rows.length;
var colCount = myTableHead.getElementsByTagName("tr")[0].getElementsByTagName("th").length;
var ExcelApp = new ActiveXObject("Excel.Application");
var ExcelSheet = new ActiveXObject("Excel.Sheet");
ExcelSheet.Application.Visible = true;
for(var i=0; i<rowCount; i++)
{
for(var j=0; j<colCount; j++)
{
str= myTableHead.getElementsByTagName("tr")[i].getElementsByTagName("th") [j].innerHTML;
ExcelSheet.ActiveSheet.Cells(i+1,j+1).Value = str;
}
}
Your question is a bit vague, so I'm guessing at what you want. Assuming your current function works as is, you can just take out the hard-coding of the table's ID and pass it in as a parameter:
function write_headers_to_excel(tableID) {
var myTableHead = document.getElementById(tableID);
// rest of your function as is
}
Then call it once for each table, though that will create a new ExcelSheet for each table.
If the idea is for all of the tables to be added to the same ExcelSheet you can pass an array of table IDs to the function something like the following. I've kept the basic structure of your function but moved the variable declarations out of the loops (since that what JavaScript does behind the scenes anyway), deleted your ExcellApp variable since it wasn't used, and moved the getElementsByTagName call out of the inner loop.
write_headers_to_excel(["headers1","headers3","headers7","etc"]);
function write_headers_to_excel(tableIDs) {
var myTableHead,
rowCount,
cols,
t,
i,
j,
rowOffset = 1,
ExcelSheet = new ActiveXObject("Excel.Sheet");
ExcelSheet.Application.Visible = true;
for (t=0; t < tableIDs.length; t++) {
myTableHead = document.getElementById(tableIDs[t]);
rowCount = myTableHead.rows.length;
for(i=0; i<rowCount; i++) {
cols = myTableHead.rows[i].getElementsByTagName("th");
for(j=0; j < cols.length; j++) {
ExcelSheet.ActiveSheet.Cells(i+rowOffset,j+1).Value = cols[j].innerHTML;
}
}
rowOffset += rowCount;
}
}
(No, I haven't tested it.)
You can get all tr elements by tag name
var rows = document.getElementsByTagName('tr');// get all rows of all tables
var table=0, TableRow=0;
for (i = 0; i < rows.length; i++) {
row = rows[i];
if (row.parentNode.tagName != 'THEAD' && row.parentNode.tagName != 'thead') {
table=table+1;
// do something here for headers
} else if (row.parentNode.tagName != 'TBODY' && row.parentNode.tagName != 'tbody')
{
TableRow=TableRow+1;
//do something here for rows
}
}