We use Kendo Grid for heads down data entry in an accounting application. We need the grid to behave similar to QuickBooks. Specifically, users should be able to:
Tab between cells
Automatically advance to the next row when tabbing off the last cell in a row.
Automatically add a new row when tabbing off the last cell of the last row.
However, we're having difficulty achieving this behavior. The last cell on the last row does not get saved before adding a new row to the bottom of the grid. If, however, we add a bogus column as the last column the proper behavior is achieved, but you have to tab through an extra, needless column which is unacceptable. The user needs to be able to enter and tab with new rows being added automatically. Here's the code:
function createGrid() {
var grid: any = $("#" + target).kendoGrid({
dataSource: gridDs,
edit: gridCellEdit,
editable: {
createAt: 'bottom'
},
filterable: true,
sortable: true,
navigatable: true,
resizable: true,
reorderable: true,
scrollable: { virtual: true },
columns: gridColumns,
dataBound: monitorKeyboard
});
}
function gridCellEdit(e) {
var input = e.container.find("input");
input.focus(function (e) {
input.select();
});
input.focus();
}
function monitorKeyboard(e) {
var grid = $('#' + e.sender.wrapper[0].id).data("kendoGrid");
$(grid.tbody).on("keydown", "td", function (e) {
// Monitor tabbing through columns
if (e.keyCode == 9) {
var row: any = $(this).closest("tr");
var rowIndex: number = $("tr", grid.tbody).index(row);
var colIndex: number = $("td", row).index(this);
var count: number = grid.dataSource.total();
var lastColIndex = row[0].cells.length - 1;
if (rowIndex == count - 1 && colIndex == lastColIndex) {
e.stopImmediatePropagation();
e.preventDefault();
grid.addRow();
grid.saveRow();
}
}
});
}
Related
I'm trying to create a menu that appears when a user hovers on a row, just like in the image below.
I did not find any built-in option to achieve this. Also tried using a custom CellRenderer function to create an element that I could move around later, but that didn't work as expected since it presented some other challenges (css wise) and was not really achieving the goal.
Is there a way to build this kind of menus in ag-Grid?
To work around the problem, you could use onCellMouseOver & onCellMouseOut methods:
var gridOptions = {
columnDefs: columnDefs,
onCellMouseOver : onCellMouseOver,
onCellMouseOut: onCellMouseOut,
...
};
Define both functions:
var onCellMouseOver = function(event){
//Get current row
var rowIndex = event.node.rowIndex;
var row = gridOptions.api.getDisplayedRowAtIndex(rowIndex);
//Set current row as not selected - in order to base on 'cellStyle' function
row.setSelected(true);
//Setup refresh params
var params = {
force: true, //Force refresh as cell value didn't change
rowNodes: [row]
};
//Refresh current row cells
gridOptions.api.refreshCells(params);
}
var onCellMouseOut = function(event){
//Get current row
var rowIndex = event.node.rowIndex;
var row = gridOptions.api.getDisplayedRowAtIndex(rowIndex);
//Set current row as not selected - in order to base on 'cellStyle' function
row.setSelected(false);
//Setup refresh params
var params = {
force: true, //Force refresh as cell value didn't change
rowNodes: [row]
};
Then define 'cellStyle' function for your column:
var columnDefs = [
{headerName: "your_column_name", field: "your_column",
cellStyle: function(params) {;
console.log('Is row selected', params.node.selected);
if (params.node.selected) {
return {display : 'none'};
} else {
return {display : 'inherit'};
}
}
}
];
You can find more about data refresh here: https://www.ag-grid.com/javascript-grid-refresh/
The full implementation of the code above might be found here: Working example
Second solution, edited after comments:
Another way is to use css classes to achieve the result.
{
headerName: "Price",
field: "price",
cellStyle: { "text-align": "center" },
cellRenderer: function(params) {
return (
"<div class='buttons'>" +
"<div class='back'>" +
params.value +
"</div>" +
"<div class='front'><button>Option A</button><button>Option B</button></div>" +
"</div>"
);
}
Buttons are shown on hover based on .ag-row-hover ag-grid class:
.front {
display: none;
}
.ag-row-hover .front {
display: inherit;
}
Working example
I want to filter a specific column on a Kendo Grid that contains an array.
The grid configuration is
var resultsGrid = this.kendoGrid({
dataSource:{
data:[],
pageSize:20
},
sortable: {
mode: "multiple",
allowUnsort: true
},
filterable: {
extra: false,
operators: {
string: {
startswith: "Starts with",
eq: "Is equal to",
neq: "Is not equal to"
}
}
},
pageable: {
pageSizes: true,
buttonCount: 5
},
scrollable: false,
columns: resultsColumns
}).data("kendoGrid");
return resultsGrid;
The columns configuration is
var resultsColumns = [
{field: "keys", filterable: true, headerTemplate: function(){return Ec.translatedLabel("authorityEntryCode");}, template: '#for(var i = 0; i < keys.length; i++){# #=keys[i].name# = #=keys[i].value# <br> #}#'},
{field: "state", filterable: true, headerTemplate: function(){return Ec.translatedLabel("state");}},
{field: "startDate", filterable: false, headerTemplate: function(){return Ec.translatedLabel("startDate");}},
{command: initGridCommands(), headerTemplate: function(){return Ec.translatedLabel("actions");}}
];
and I want to filter column with the field keys
The field keys is an array of object that contains name and value and I want to filter with the value.
For example
Example of the Grid
So when the user adds a1 as in the screenshot to be filtered the first row.
Please if you have an answer on that it will help me alot, thanks.
Unfortunately this is rather difficult to do in my opinion. The general process would require binding to the filterMenuInit event, removing the contents of the default filter pop up with your own form and then performing the filtering manually on the specific fields you want. I'm not aware of a built in way to perform filtering on an array field. A hacky version overriding only what's required could be like:
filterMenuInit: function(e) {
if (e.field == "keys") {
var filterButton = e.container.find("button[type=submit]");
var clearButton = e.container.find("button[type=reset]");
var dataSource = jQuery("#grid").data("kendoGrid").dataSource;
//Get rid of default filter button...
filterButton.remove();
clearButton.parent().prepend("<button type='button' class='k-button k-primary'>Filter</button>");
var filterText = e.container.find(".k-textbox");
filterButton = e.container.find("button[type=button]");
filterButton.click(function(e) {
e.preventDefault();
dataSource.filter([
{
field: 'keys',
operator: function (items, filterValue) {
for(var i = 0; i < items.length; i++) {
if(items[i].value == filterText.val()) {
return true;
}
}
return false;
},
value: filterText.val()
}
]);
});
clearButton.click(function() {
dataSource.filter([]); //clear filters...
});
}
},
This code is quick and dirty so I'd encourage you to read the docs concerning the filterMenuInit event and data source filtering to come up with a cleaner solution. Here is a snippet as well.
I have a kendo grid in which fields are editable based on some condition like:
//var isEditable= some condition
fields: {
Id: { type: "int", editable: false },
Amount: {type: "number", editable: isEditable},
}
I want to get all editable cells and perforn something on them before they are displayed.
Currently i'm using dataBound event iterate rows and cells and find desired cells
dataBound: function(e){
var rows = e.sender.tbody.children();
for (var j = 0; j < rows.length; j++) {
var row = $(rows[j]);
if (isEditable){
var cell1 = row.children().eq("hardcoded index");
var cell2 = row.children().eq("hardcoded index 2");
var cell3 = row.children().eq("hardcoded index 3");
......
......
// perform action
}
}
Is there a better approach to achieve that?
Since you want to do something to the editable element before display, I would use a template to accomplish that. Here is an example that turns the background color red if the Amount field is editable:
<script id="editableTemplate" type="text/x-kendo-template">
#if (Editable) { #
<div style='background-color:red'>#=Amount#</div>
# } else { #
<div>#=Amount#</div>
# } #
<script>
Another way to approach this is to take parameters by wrapping the template in a function:
function foo(isEdit, value) {
if(isEdit){
return "<div style='background-color:red'>" + value + "</div>";
} else {
return "<div>" + value + "</div>";
}
}
You can then set the column template as:
{
field: "Amount",
type: "number",
template: function(data) {
return kendo.template("#=foo(isEditable, Amount)#")
},
editable: isEditable
}
Here is a basic fiddle.
On my html page I have a table that has dynamically added table rows once a button is clicked. Here is the code behind it. What I am trying to do is add a slider bar to one of the td's. After that I will be taking the value of that slider bar and put it into a cookie. My question is why doesnt this work?
$(function(){
$( "#tabs" ).tabs();
});
$(function() {
$( document ).tooltip();
});
$(function () {
$("#labSlider").slider(
{
orientation:"horizontal",
range:"min",
min: 0,
max: 25,
value: 15,
slide: function(event, ui){$("#amount").val(ui.value);}
}
);
$("#amount").val($("#labSlider").slider("value"));
});
function labAddRow(){
var labRows = $('#labTable tr').length; //how many rows in table
if (labRows <= 10){
var newRow = $('<tr><td>' + labRows + '</td><td><div id ="labSlider"></div></td><td><input type="text" class="text_field" maxlength="2" size="2" onchange="calculateGrade()" title= "What did you score on lab '+ labRows+'?"/></td><td>' + "/25" + '</td></tr>'); //create the new row
$('#labTable').append(newRow);
}
}
Passing a function to jQuery causes it to be called when the DOM is ready. It doesn’t execute automatically for DOM changes or anything like that.
Learn to love the DOM! Functions too! You can make a function to create an appropriate slider for an element, then use it whenever you add a row. And I’m not sure what #amount is, but it might need to be corrected to match element.
$(function() {
$('#tabs').tabs();
$(document).tooltip();
});
function createSlider(element) {
$(element).slider(
{
orientation:"horizontal",
range:"min",
min: 0,
max: 25,
value: 15,
slide: function(event, ui){$("#amount").val(ui.value);}
}
);
$("#amount").val($(element).slider("value"));
}
function labAddRow() {
var labRows = $('#labTable tr').length; //how many rows in table
if (labRows <= 10) {
var textField = $('<input>', {
type: 'text',
'class': 'text_field',
maxlength: 2,
size: 2,
title: 'What did you score on lab ' + labRows + '?'
}).on('change', calculateGrade);
var slider = $('<div>', { id: 'labSlider' });
var newRow = $('<tr>').append(
$('<td>').text(labRows),
$('<td>').append(slider),
$('<td>').append(textField),
$('<td>', { text: '/25' })
);
createSlider(slider);
$('#labTable').append(newRow);
}
}
I am trying to get the row item values (name, email, age) but I'm only able to get the first item by using the code below.
How can I get other row text by changing tr:eq(1) code or is there any other way to get two items value?
$("#grid_").kendoDropTarget({
drop: function (e) {
var data = grid.dataItem("tr:eq(1)");
// I only get first row but I need to dynamically get any row items.
alert(data.name);
}
});
plz try this..
var entityGrid = $("#DataGrid").data("kendoGrid");
var data = entityGrid.dataSource.data();
var totalNumber = data.length;
for(var i = 0; i<totalNumber; i++) {
var currentDataItem = data[i];
VersionIdArray[i] = currentDataItem.VersionId;
}
Thanks Sanjay however I was looking to just select a row items and this is what I got:
//Selecting Grid
var gview = $("#grid").data("kendoGrid");
//Getting selected item
var selectedItem = gview.dataItem(gview.select());
//accessing selected rows data
alert(selectedItem.email);
So it worked out perfect.
if your grid is set to selectable: true, use the following:
var mygrid = $("#grid").kendoGrid({
selectable: true
});
mygrid.on("click", "tr", function() {
var datarowindex = mygrid.data("kendoGrid").items().index(mygrid.data("kendoGrid").select());
var datarowid = mygrid.data("kendoGrid").dataItem(mygrid.data("kendoGrid").select()).MyId;
alert("index: " + datarowindex + " | value: " + datarowid);
});
if your Kendo UI Grid is set to selectable: false, use the following:
var mygrid = $("#grid").kendoGrid({
selectable: false
});
mygrid.on("click", "tr", function() {
var datarowindex = mygrid.data("kendoGrid").items().index($(this));
var datarowid = mygrid.data("kendoGrid").dataItem($(this).closest("tr")).MyId;
alert("index: " + datarowindex + " | value: " + datarowid);
});
where MyId is the property you are looking for.
I usually use the model from the event. Sometimes, actually very rarely, the row gets deselected and so, the .select() will return a 0 length object which will throw error while trying to access undefined properties.
You might be safer using: e.model.name