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"]);
Related
I have a scenario where a user is able to select multiple cells on a grid. I would like to validate the users selection and deselect any invalid cells. I am trying to achieve this in the change function on my grid. My current approach is to get the currently selected cells, determine which cells are valid and select the list of valid cells I have found.
According to Kendo's documentation, the grid.select() function does take a parameter, but only takes an array of rows as a parameter. This explains why the below isnt working.
I need a solution which allows me to set which cells are highlighted and also retrieve the highlighted cells (I currently can get selected cells via the grid.select() method).
Is there a reasonable way to do this?
$scope.myKendoGridOptions = {
selectable: "multiple, cell",
change: function (e) {
if (!$scope.jsSelectChangeEvent) {
var grid = $('#myKendoGrid').data("kendoGrid");
var selectedItems = grid.select();
if (selectedItems.length > 1) {
var validItems = [];
var validRowIndex = selectedItems[0].closest("tr").rowIndex;
for (var i = 0; i < selectedItems.length; i++) {
if (selectedItems[i].closest("tr").rowIndex === validRowIndex) {
validItems.push(selectedItems[i]);
}
}
//Avoids Infinite loop
$scope.jsSelectChangeEvent = true;
//im expecting this to select my valid cells, but it has no effect
grid.select(validItems);
}
} else {
$scope.jsSelectChangeEvent = false;
}
}
};
The grid.select(validItems) does accept cells as a parameter. The problem is that I need to call grid.clearSelect() prior to calling grid.select to get the desired effect.
I'm using Kendo multi select as follow but i can't get selected values
var multiselect = $("#SelectRoles").data("kendoMultiSelect");
var selectedData= [];
var items = multiselect.value();
for (var itm in items)
{
selectedData.push(itm);
}
but array selectedData return indices of items in multiselect not values .
You can also assign the array, returned from the value() method, directly to the variable, e.g.:
var ms = $("#multiselect").kendoMultiSelect({
value: ["1", "2"]
}).data('kendoMultiSelect');
var selectedItems = ms.value();
console.log(selectedItems); // ["1", "2"]
Use this other one returns indices.
var multiselect = $("#SelectRoles").data("kendoMultiSelect");
var selectedData= [];
var items = multiselect.value();
for (var i=0;i<items.length;i++)
{
selectedData.push(items[i]);
}
Your original code doesn't look wrong. Are you sure you are getting only indices? Perhaps you should post your MultiSelect code as well. I found this question because I had the same problem and used the other answers for reference, but I found them overcomplicated. So let me answer in another complicated way :)
Here's what I've got. I know it's more code than you need, but I think it's important to see the full picture here. First let me set this up. There's a problem with the Kendo().MultiSelect.Name("SomeName") property if you are using it more than once. "Name" sets not only the html name, but the id as well, and you never want two ids with the same identifier. So in my code, I am appending a unique Id to my MultiSelect.Name property to ensure a unique id. I am putting the MultiSelect in each row of a table of people. I am showing this to make sure you are using the DataValueField property so you are able to get the selected values (not the text you see in the ui). If you are just showing a list of text values with no id behind them, perhaps that is why you are getting the wrong data?
#foreach (var cm in Model.CaseMembers)
{
<tr>
<td>
#(Html.Kendo().MultiSelect()
.Name("IsDelegateFor" + cm.CaseMemberId)
.Placeholder("is a delegate for..")
.DataTextField("FullName")
.DataValueField("CaseMemberId")
.BindTo(Model.Attorneys)
)
</td>
</tr>
}
then, later on, in my jQuery where I attempt to extract out the DataValueField (CaseMemberId), which is the array of selected values of the MultiSelect...
var sRows = [];
$('#cmGrid tr').each(function () {
// 'this' is a tr
$tr = $(this);
// create an object that will hold my array of selected values (and other stuff)
var rec = {};
rec.IsADelegateFor = [];
// loop over all tds in current row
$('td', $tr).each(function (colIndex, col) {
if (colIndex === 3) {
// make sure our MultiSelect exists in this td
if ($(this).find("#IsDelegateFor" + rec.CaseMemberId).length) {
// it exists, so grab the array of selected ids and assign to our record array
rec.IsADelegateFor = $(this).find("#IsDelegateFor" + rec.CaseMemberId).data("kendoMultiSelect").value();
}
}
}
// add this tr to the collection
sRows.push(rec);
}
so this is all a super verbose way of saying that this single line, as the other people mentioned works perfectly to grab the ids. There is no need to iterate over the .value() array and push the contents to another array!
rec.IsADelegateFor = $(this).find("#IsDelegateFor" + rec.CaseMemberId).data("kendoMultiSelect").value();
So in your original code, there is no reason the following should not work,
var multiselect = $("#SelectRoles").data("kendoMultiSelect");
var selectedData = [];
selectedData = multiselect.value();
console.log(selectedData);
unless
you don't have your MultiSelect set up properly in C# with DataValueField
you have multiple MultiSelects on the page with the exact same id and it's reading from a different one than you think.
You don't even have value fields, just a list of text.
var selected = $("#multi").data("kendoMultiSelect").value();
The solution given by volvox works.
Below is jquery version,
var multiselect = $("#SelectRoles").data("kendoMultiSelect");
var selectedData= [];
var items = multiselect.value();
$.each(items ,function(i,v){
selectedData.push(v);
});
I am using wijmo flex grid to create a tree view for my data, I am able to find whether a specific node has children or not and what is the level of the node but I am unable to go to the parent node from a given node. The index for each row is also being retrieved.
Any insights on the topic would be highly helpful.
$scope.selectionChanged = function(sender,args){
var index = sender.selection.row;
var temp;
console.log(index);
temp = sender._rows[index]._data;
console.log(temp.reports);
};
FlexGrid rows come in two flavors: regular rows (Row objects) and nodes (GroupRow objects). Regular rows have no "level", but GroupRow objects do have a "level" property that you can use to get the node's level.
To get a row's parent node, you should scan the grid's rows collection up until you find a node that has a "level" smaller than the one you started with.
Here's a fiddle the demonstrates:
http://jsfiddle.net/Wijmo5/8n2yde6f/
Check out the implementation of the "getParentNode" method, that should be what you're looking for:
// gets the parent row for a given FlexGrid row.
// returns the parent row or null if original row doesn't have a parent.
function getParentNode(row) {
// get row level
var startLevel = row instanceof(wijmo.grid.GroupRow) ? row.level : -1;
var startIndex = row.index;
// travel up to find parent node
for (var i = startIndex - 1; i >= 0; i--) {
var thisRow = row.grid.rows[i],
thisLevel = thisRow instanceof(wijmo.grid.GroupRow) ? thisRow.level : -1;
if (thisLevel > -1) {
if (startLevel == -1 || (startLevel > -1 && thisLevel < startLevel)) {
return thisRow;
}
}
}
// not found
return null;
};
Hope this is useful.
What you want to do is access the dataItem of the selected row and see if it contains children, using the FlexGrid's childItemPath that you set.
Here is a working sample: http://jsfiddle.net/banzor/700e6bn2/1/
And here is the code for my selectionChanged event.
$scope.selectionChanged = function(sender, args){
var index = args.row;
var row = args.panel.rows[index].dataItem;
var childPath = sender.childItemsPath;
var children = row[childPath];
if (children && wijmo.isArray(children)) {
console.log("Has items: " + children.length);
}
};
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
I have 2 arrays, one with cities, and another array with ID's...
I would like to loop both, and then output some HTML
Right now im only looping cities, and then appending some HTML to a as following:
if ($jq(".area").length > 0) {
var region = $jq(".hiddenFieldRegion").val();
var cityIDs = $jq(".hiddenFieldChosenAreas").val();
var strcities = $jq(".hiddenFieldChosenCities").val();
//var cities = strcities.split('|');
//Set region to be marked
if (region) {
$jq(".mapdk").attr("class", region);
}
//Appending to searchform
if (strcities) {
$jq.each(strcities.toString().split("|"), function (k, v) {
var v = v.split("|");
$jq("<option selected />").text(v[0]).appendTo("select.chosen-cities");
});
}
}
I would like the place where im appending to the searchform, add an attribute value, with an ID from the cityIDs array...
So i get some HTML like:
<option value="XXX">CityName</option>
Is that possible?
Assuming your cities IDs and your cities names are orderred in the same way, you can access each name/id by this index in the arrays :
var cityIDs = $jq(".hiddenFieldChosenAreas").val();
var strcities = $jq(".hiddenFieldChosenCities").val();
var cityIDs_array = cityIds.split("|");
var strcities_array = strcities.split("|");
....
if (strcities) {
$jq.each(strcities_array, function (k, v) {
//var v = v.split("|"); Unnecessary if strcities is like "city1|city2|city3"
$jq("<option value='"+cityIDs_array[k]+"' />").text(v).appendTo("select.chosen-cities");
});
}
In this, way, you can access each city ID in your loop.
But I think you should store city name and city id in the same string. For example "city1:1|city2:2|city3:3". Then, with some use of split function, you'll get id and name.
Assuming the order is the same in both arrays you can use the index parameter (k) of the each function:
if (strcities) {
var aCityIDs = cityIDs.split("|");
$jq.each(strcities.toString().split("|"), function (k, v) {
var v = v.split("|");
var id = aCityIDs[k];
$jq("<option selected value='"+id+"' />").text(v[0]).appendTo("select.chosen-cities");
});
}
This is easy enough if your two arrays are in parity with one another with regards to indexing - loop over one, then look up a value in the other array at the same index.
//simulate environment
var strcities = 'one|two|three',
cityIDs = '1|2|3';
//main code
var ids = cityIDs.split('|');
$.each(strcities.toString().split("|"), function (index, val) {
var opt = $("<option />", {value: ids[index], text: val}).appendTo("select.chosen-cities");
/* if (index == 2) opt.prop('selected', 'selected'); /*
});
A few points of note:
you were splitting on | once in the $.each and again inside it. Since this would be illogical (any | would have disappeared after the first split) I removed it.
you were adding selected to each option. If you simply want the first option to be selected, you don't need to add anything in. If you want a specific option to be selected, see the line I commented out, which selects the 3rd element.
http://jsfiddle.net/DhH3U/
Another example:
var strict = ('aaa|eeee|dddd').split('|');
var id = ('1|2|3').split('|');
$.each(strict, function (k) {
$("<option selected />").text(strict[k]).appendTo("select.chosen-cities").attr('value',id[k]);
});