How to programmatically create a new row and put that row in edit mode in Kendo grid - kendo-ui

In my Kendo grid I am trying to put the 'create new item' button in the header (title) of the command column instead of the toolbar. Here is part of my grid definition:
var grid = $("#grid").kendoGrid({
columns: [{ command: { name: "edit", title: "Edit", text: { edit: "", cancel: "", update: "" } },
headerTemplate: "<a onclick ='NewItemClick()' class='k-button k-button-icontext k-create-alert' id='new-item-button' title='Click to add a new item'><div>New Item</div></a>"},
My question is: how to create a new row and put that row in edit mode in 'NewItemClick()'

There are some troublesome scope issues when you try to bind the click event in the template definition itself.
Instead, it is easier to assign the link an ID, and then bind the click event later. Notice that I've given it id=create.
headerTemplate: "<a id='create' class='k-button k-button-icontext k-create-alert' id='new-item-button' title='Click to add a new item'><div>New Item</div></a>"
Then in document ready, I bind the click event:
$("#create").click(function () {
var grid = $("#grid").data("kendoGrid");
if (grid) {
//this logic creates a new item in the datasource/datagrid
var dataSource = grid.dataSource;
var total = dataSource.data().length;
dataSource.insert(total, {});
dataSource.page(dataSource.totalPages());
grid.editRow(grid.tbody.children().last());
}
});
The above function creates a new row at the bottom of the grid by manipulating the datasource. Then it treats the new row as a row "edit". The action to create a new row was borrowed from OnaBai's answer here.
Here is a working jsfiddle, hope it helps.

I would like to complete on gisitgo's answer. If your datasource takes some time to update, when calling page(...), then the refresh of the grid will cancel the editor's popup. This is averted by binding the call to editRow to the "change" event :
var grid = $("#grid").data("kendoGrid");
if (grid) {
//this logic creates a new item in the datasource/datagrid
var dataSource = grid.dataSource;
var total = dataSource.data().length;
dataSource.insert(total, {});
dataSource.one("change", function () {
grid.editRow(grid.tbody.children().last());
});
dataSource.page(dataSource.totalPages());
}
NB: This approach will yield problems if your grid is sorted because the new row will not necessarily be at the end

I have found that issues might appear if you have multiple pages, such as the inserted row not opening up for edit.
Here is some code based on the current index of the copied row.
We also edit the row based on UID for more accuracy.
function cloneRow(e) {
var grid = $("#grid").data("kendoGrid");
var row = grid.select();
if (row && row.length == 1) {
var data = grid.dataItem(row);
var indexInArray = $.map(grid.dataSource._data, function (obj, index)
{
if (obj.uid == data.uid)
{
return index;
}
});
var newRowDataItem = grid.dataSource.insert(indexInArray, {
CustomerId: 0,
CustomerName: null,
dirty: true
});
var newGridRow = grid.tbody.find("tr[data-uid='" + newRowDataItem.uid + "']");
grid.select(newGridRow);
grid.editRow(newGridRow);
//grid.editRow($("table[role='grid'] tbody tr:eq(0)"));
} else {
alert("Please select a row");
}
return false;
}

Related

Kendo grid checkbox get last selected value

I have kendo grid with a select option. My question is how do I get ProductName from the last selected row? Example like this. I have tried with this but row is null. Appreciate your help.
FULL DEMO IN DOJO
function onChange(e) {
var rows = e.sender.select();
rows.each(function(e) {
var grid = $("#grid").data("kendoGrid");
var row = $(e.target).closest("tr"); //<-- this value capture null
var dataItem = grid.dataItem(row);
var productName = dataItem.ProductName;
alert(productName);
})
};
You have incorrect parameters in the function in each.
This causes name masking: https://en.wikipedia.org/wiki/Name_resolution_(programming_languages)#Name_masking
At the outer level, you have:
function onChange(e) {
Then, in the third line, you have:
rows.each(function(e) {
That's two functions of e. So which e counts? The inner one masks the outer one.
The correct arguments for the inner function are:
rows.each(function(index, row) {
Now, you're already iterating over rows, so you have the row, you don't need to look for it with any closest().
You also have the grid, it's e.sender, because you're in a grid event.
That gives you the following code:
function onChange(e) {
var rows = e.sender.select();
rows.each(function(index, row) {
console.log(row);
var dataItem = e.sender.dataItem(row);
console.log(dataItem);
var productName = dataItem.ProductName;
console.log(productName);
})
};
Demo
In the future, be wary of the difference between Kendo events and jQuery events.
Just use grid.select() inside grids change function:
function onChange(e) {
var grid = $("#grid").data("kendoGrid");
var selectedItem = grid.dataItem(grid.select());
console.log(selectedItem.ProductName)
};
Example: Get last selected item

Drag rows in jqGrid

I want to implement draggable rows feature in jqGrid and it's working also using option
$('#MyGrid').jqGrid('gridDnD', {
connectWith: '#MyGrid2'});
Now, my client want to show "Drag here" text in target grid row at the position where the row will be dragged from existing to new one, how can I show this text while dragging row from source to target? Any help is appreciated...
I find your question interesting. So I modified the old demo from the answer and created the demo which demonstrates a possible implementation. The grid during dropping looks like on the picture below:
It uses the following code
$("#grid1").jqGrid("gridDnD", {
connectWith: "#grid2",
drop_opts: {
activeClass: "",
hoverClass: ""
},
onstart: function (ev, ui) {
ui.helper.addClass("ui-widget ui-widget-content")
.css({
"font-size": "11px",
"font-weight": "normal"
});
},
onstop: function (ev, ui) {
$("#dragHelper").hide();
},
droppos: "afterSelected", // "beforeSelected"
beforedrop: function (e, ui, getdata, $source, $target) {
var names = $target.jqGrid("getCol", "name2");
if ($.inArray(getdata.name2, names) >= 0) {
// prevent data for dropping
ui.helper.dropped = false;
alert("The row \"" + getdata.name2 + "\" is already in the destination grid");
}
$("#dragHelper").hide();
$("#grid2").jqGrid("setSelection", this.id, true, e);
},
ondrop: function (ev, ui, getdata) {
var selRow = $("#grid2").jqGrid("getGridParam", "selrow"),
$tr = $("#" + $.jgrid.jqID(selRow));
if ($tr.length > 0) {
$("#grid2").jqGrid("setSelection", $("#grid2")[0].rows[$tr[0].rowIndex + 1].id, true);
}
}
});
// make every row of the destination grid droppable and select row on over
$("#grid2 tr.jqgrow").droppable({
hoverClass: "ui-state-hover",
over: function (e, ui) {
$("#grid2").jqGrid("setSelection", this.id, true, e);
$("#dragHelper").show().position({my: "right center", at: "left bottom", of: $(this)});
}
});
I reserve some place for the tooltip "Drag here ↣" on the left of the grid and marked the row under the moved row additionally to make the position of the dropped row mostly clear. I used free jqGrid which have support of "afterSelected" and "beforeSelected" position of addRowData originally suggested in the answer. So I used droppos: "afterSelected". The dropped rows will be inserted after the selected row.

Remote update cell content in kendo grid

Here I have a code to update a cell contet when pressing a button.
It works fine, but it doesn't set the flag, that indicates, that the cell has been changed.
It should look like this with the litle red triangle:
The code:
<a id="button" href="#">Click me</a>
<div id="grid"></div>
<script>
var dataSource, grid;
$(document).ready(function () {
dataSource = new kendo.data.DataSource({
data: [
{ category: "Beverages", name: "Chai", price: 18},
{ category: "Seafood", name: "Konbu", price: 6}
],
})
grid = $("#grid").kendoGrid({
dataSource: dataSource,
editable: true,
}).data("kendoGrid");
$('#button').click(function (e) {
var data = grid.dataItem("tr:eq(1)");
data.set('category', 'Merchandice');
});
});
</script>
Update:
Here is the update based on #tstancin: Kendo example.
Thank you for the answer - I had thought of it to.
I am wondering if it's possible to do the update in a more clean way with some binding through som MVVM perhaps?
Kind regards from Kenneth
If that's all you want then you should expand your button click code with the following:
$('#button').click(function (e) {
var data = grid.dataItem("tr:eq(1)");
data.set('category', 'Merchandice');
$("#grid tr:eq(1) td:eq(1)").addClass("k-dirty-cell");
$("#grid tr:eq(1) td:eq(0)").prepend("<span class='k-dirty'></span>");
});
But if you, for instance, manually change the value of name column from Chai to something else, and then click the click me button, the dirty marker in the name column will disappear.
You should use flags for every cell and set them before data.set(). Then, in the grid's dataBound event you should inspect every cell's value and assign the dirty marker if it's needed. For manual changes you should handle the save event and set flags there.
I wrote a script that makes it posible to use a call like this:
SetCellData(id, columnName, value);
So with an id, a columnName and a value, I can update a value in a grid and the flag will be set on the cell.
function SetCellData(id, columnName, value) {
var dataItem = grid.dataSource.get(id);
dataItem.set(columnName, value);
var columnIndex = GetColumnIndex(columnName);
if (columnIndex > -1) {
var cell = $('tr[data-uid*="' + dataItem.uid + '"] td:eq(' + columnIndex + ')')
if (!cell.hasClass("k-dirty-cell")){
cell.prepend("<span class='k-dirty'></span>");
cell.addClass("k-dirty-cell");
}
}
}
function GetColumnIndex(columnName) {
var columns = grid.columns;
for (var i = 0; i < columns.length; i++)
if (columns[i].field == columnName)
return i;
return -1;
};
I have the code here : example

KendoUI Treeview Select Node On Databound

I have a list view I want the user to be able to double click which will change a treeview's datasource and select a treeview node. While I get the ID of the item I want to select, execute the code I think should select the node on the treeview, it does not select. Any thoughts would be appreciated
Listview Double Click Event:
function setItemDoubleClickEvent() {
$(".machineInstances").on("dblclick", function () {
var listView = $("#listView").data("kendoListView");
var idx = $(this).index();
var item = listView.dataSource.view()[idx];
$('#selectedNodeId').val(item.InstanceId);
// Remove the current datasource to remove any existing filtering
$("#treeview").data("kendoTreeView").setDataSource([]);
// Set the hidden input so OnData knows what to highlight
$('#selectedNodeId').val(item.InstanceId);
// Set the new datasource for the tree
$("#treeview").data("kendoTreeView").setDataSource(instanceDataSource); });
}
TreeView Declaration:
var treeview = $("#treeview").kendoTreeView({
dataTextField: "Name"
, select: onSelect,
dataBound: ondata
}).data("kendoTreeView"),
DataBound Function:
function ondata() {
//alert($('#selectedNodeId').val());
var selected = $('#selectedNodeId').val();
if (selected != "") {
var node = treeview.findByUid(selected)
$("#treeview").data("kendoTreeView").select(node);
$('#selectedNodeId').val("");
}
}
Thank you,
Drew

Kendoui grid : Remember expanded detail grids on refresh [duplicate]

I have a scenario with grid within grid implemented using the detailInit method. Here when user makes edit, i do some calculations that will change the data in the both parent and child. and then to refresh data, i will call the datasource.read to render data. this works and the data is displayed, however any detail grid which are expanded will be collapsed, is there any way i can prevent this from happening.
To answer this and another question:
"I figured out how to set the data in the master from the child BUT, the
whole table collapses the child grids when anything is updated, this is a
very annoying behavior, is there anyway I can just update a field in
the master table without it collapsing all the child elements?
(basically, update the column, no mass table update)"
in another thread at: telerik
This is extremely annoying behavior of the Kendo Grid and a major bug. Since when does a person want the sub-grid to disappear and hide a change that was just made! But this isn't the only problem; the change function gets called a Fibonacci number of times, which will freeze the browser after a significant number of clicks. That being said, here is the solution that I have come up with:
In the main grid
$('#' + grid_id).kendoGrid({
width: 800,
...
detailExpand: function (e) {
var grid = $('#' + grid_id).data("kendoGrid");
var selItem = grid.select();
var eid = $(selItem).closest("tr.k-master-row").attr('data-uid')
if (contains(expandedItemIDs, eid) == false)
expandedItemIDs.push(eid);
},
detailCollapse: function (e) {
var grid = $('#' + grid_id).data("kendoGrid");
var selItem = grid.select();
var eid = $(selItem).closest("tr.k-master-row").attr('data-uid')
for (var i = 0; i < expandedItemIDs.length; i++)
if (expandedItemIDs[i] == eid)
gridDataMap.expandedItemIDs.splice(i, 1);
},
Unfortunately globally we have:
function subgridChange() {
var grid = $('#' + grid_id).data("kendoGrid");
for (var i = 0; i < expandedItemIDs.length; i++)
grid.expandRow("tr[data-uid='" + expandedItemIDs[i] + "']");
}
function contains(a, obj) {
for (var i = 0; i < a.length; i++)
if (a[i] === obj) return true;
return false;
}
expandedItemIDs = [];
Now the 'subgridChange()' function needs to be called every time a change is made to the subgrid.
The problem is that the number of times the change function in the subgrid gets called increases exponentially on each change call. The Kendo grid should be able to call a stop propagation function to prevent this, or at least give the programmer access to the event object so that the programmer can prevent the propagation. After being completely annoyed, all we have to do is to place the 'subgridChange()' function in the subgrid 'datasource' like so:
dataSource: function (e) {
var ds = new kendo.data.DataSource({
...
create: false,
schema: {
model: {
...
}
},
change: function (e) {
subgridChange();
}
});
return ds;
}
I also had to place the 'subgridChange()' function in the Add button function using something like this
$('<div id="' + gridID + '" data-bind="source: prodRegs" />').appendTo(e.detailCell).kendoGrid({
selectable: true,
...
toolbar: [{ template: "<a class='k-button addBtn' href='javascript://'><span class='k-icon k-add' ></span> Add Product and Region</a>" }]
});
$('.addBtn').click(function (event) {
...
subgridChange();
});
When a user selects a row, record the index of the selected row. Then after your data refresh, use the following code to expand a row
// get a reference to the grid widget
var grid = $("#grid").data("kendoGrid");
// expands first master row
grid.expandRow(grid.tbody.find(">tr.k-master-row:nth-child(1)"));
To expand different rows, just change the number in the nth-child() selector to the index of the row you wish to expand.
Actually all that is needed is the 'subgridChange()' function in the main grid 'dataBound' function:
$('#' + grid_id).kendoGrid({
...
dataBound: function (e) {
gridDataMap.subgridChange();
}
});
Different but similar solution that i used for same problem:
expandedItemIDs = [];
function onDataBound() {
//expand rows
for (var i = 0; i < expandedItemIDs.length; i++) {
var row = $(this.tbody).find("tr.k-master-row:eq(" + expandedItemIDs[i] + ")");
this.expandRow(row);
}
}
function onDetailExpand(e) {
//refresh the child grid when click expand
var grid = e.detailRow.find("[data-role=grid]").data("kendoGrid");
grid.dataSource.read();
//get index of expanded row
$(e.detailCell).text("inner content");
var row = $(e.masterRow).index(".k-master-row");
if (contains(expandedItemIDs, row) == false)
expandedItemIDs.push(row);
}
function onDetailCollapse(e) {
//on collapse minus this row from array
$(e.detailCell).text("inner content");
var row = $(e.masterRow).index(".k-master-row");
for (var i = 0; i < expandedItemIDs.length; i++)
if (expandedItemIDs[i] == row)
expandedItemIDs.splice(i, 1);
}
function contains(a, obj) {
for (var i = 0; i < a.length; i++)
if (a[i] === obj) return true;
return false;
}

Resources