Kendo UI Grid handling missing values in column templates - javascript

I use Kendo UI Grid for displaying array data with objects having some fields missing. Here is js code:
var arr = [{b: "b1"}, {a: "a2", b: "b2"}];
$("#grid").kendoGrid({
dataSource: arr,
columns: [
{
title: "The A column",
field: 'a'
}, {
title: "The B column",
template: '<i>#=b#</i>'
}]
});
In this example the grid works well and displays missing "a" value in first row as empty cell.
When working with column template:
$("#grid").kendoGrid({
dataSource: arr,
columns: [
{
title: "The A column",
template: '<b>#=a#</b>'
}, {
title: "The B column",
template: '<i>#=b#</i>'
}]
});
It displays an error in console: Uncaught ReferenceError: a is not defined.
Even replacing template with:
template: '<b>#=a || ""#</b>'
expression instead does not help, so I have to manually set the missing values to empty string before constructing the table. Is there way to avoid this?

Instead of:
template: '<b>#=a || ""#</b>'
You should use:
template: '<b>#=data.a || ""#</b>'
Where data is predefined by KendoUI and is equal to the row data. Otherwise JavaScript doesn't know that a should be part of the data and thinks that it is a variable per-se throwing the error.
You can see it running in the following snippet
$(document).ready(function() {
var arr = [{b: "b1"}, {a: "a2", b: "b2"}];
$("#grid").kendoGrid({
dataSource: arr,
columns: [
{
title: "The A column",
template: '<b>#= data.a || ""#</b>'
}, {
title: "The B column",
template: '<i>#=b#</i>'
}]
});
});
<link rel="stylesheet" href="http://cdn.kendostatic.com/2015.1.429/styles/kendo.common.min.css">
<link rel="stylesheet" href="http://cdn.kendostatic.com/2015.1.429/styles/kendo.default.min.css">
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://cdn.kendostatic.com/2015.1.429/js/kendo.all.min.js"></script>
<div id="grid"></div>

Related

Append element to Datatable header

What I'd like to do is add a radio button next to the Search Bar on my datatable to allow searching by just one column, Store Number.
I was referred to drawCallback but I don't believe this does what I expect it to do. All the answers I find seem to be appending elements to rows/cols in the datatable, but not the header itself.
The selector for this header is #store-table_wrapper.
$('#store-table').DataTable({
"columnDefs": [{
"targets": [7, 8],
"visible": false,
"drawCallback": function() {
$('<input type="radio" name="store-number-filter-selector" />').appendTo('#store-table_wrapper');
}
}]
});
I believe, getting your radio button displayed you're half-way through, the really challenging part is to disable default search bar, since you're unlikely to override its default behavior (to search through the entire table).
However, you may use your own, custom searchbar, like on the following DEMO:
//define source data
const srcData = [
{id: 1, name: 'apple', category: 'fruit'},
{id: 2, name: 'raspberry', category: 'berry'},
{id: 3, name: 'carrot', category: 'vegie'}
];
//define dataTable object
const dataTable = $('#mytable').DataTable({
sDom: 't',
data: srcData,
columns: [
{data: 'id', title: 'id'},
{data: 'name', title: 'name'},
{data: 'category', title: 'category'}
],
//modify header nodes, by appending radios
initComplete: function() {
const table = this.api();
[1,2].forEach(column => table.column(column).header().innerHTML += `<input type="radio" name="searchflag" value="${column}" class="searchflag"></input>`);
}
});
//prevent sorting change upon radio click
$('input.searchflag').on('click', function(event) {
//clear search upon choosing the other radio
$('#searchfield').val('');
dataTable.search('').columns().search('').draw();
event.stopPropagation();
});
//searchbar keyup callback
$('#searchfield').on('keyup', function() {
//grab checked radio button value or search the entire table by default
let targetColumn = null;
targetColumn = $('input.searchflag:checked').val();
if(!targetColumn){
dataTable.search($(this).val()).draw();
}
else {
dataTable.column(targetColumn).search($(this).val()).draw();
}
})
input.searchflag {
float: left;
}
<!doctype html>
<html>
<head>
<script type="application/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script type="application/javascript" src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="demo.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css">
</head>
<body>
<input id="searchfield"></input>
<table id="mytable"></table>
</body>
</html>

Displaying object inside object in kendo grid

I new with kendo and I have problem with kendo grid. I have structure like this
var data = [{
key1: value1,
key2: value2,
objectInside: [{
insideKey1: insideValue1,
insideKey2: insideValue2,
insideKey3: insideValue3
},
{
insideKey1: insideValue1a,
insideKey2: insideValue2a,
insideKey3: insideValue3a
},
{
insideKey1: insideValue1b,
insideKey2: insideValue2b,
insideKey3: insideValue3b
}]
}];
and I need to create kendo grid and fill it with objectInside elements. For now I can display one of the element of the array:
var grid = $("#grid").kendoGrid({
pageable: true,
selectable: "row",
dataSource: data
columns : [
{ field: "objectInside.insideKey1[0]", title: "Value1:" },
{ field: "objectInside.insideKey2[0]", title: "Value2:" },
{ field: "objectInside.insideKey3[0]", title: "Value3:" }
]
}).data("kendoGrid");
But I do not have idea how to reach all the elements. Without indexing it doesn't work. It is possible to make loop here? I thought about making another variable contain only objectInside and try to read only this element but I failed here too. I try to do it like this:
var newData = data.objectInside;
or
var newData = JSON.stringify(data.objectInside);
Could anyone give me a hint how to deal with it?
Try this template:
{ template: "#= data.objectInside[0].insideKey1 #", title: "Value1" },
{ template: "#= data.objectInside[1].insideKey2 #", title: "Value2" },
{ template: "#= data.objectInside[2].insideKey3 #", title: "Value3" }
Demo
But this will show only one row, because the grid data contains one item in the first level. You can do like this to show all your data inside insideObject.

Cell values not displayed correctly in Kendo UI Grid using custom DropDownList editor

The title may be confusing, but I had a bit of trouble explaining myself in a single sentence, so read on for a bit more detailed scenario.
I am trying to get a Kendo UI DropDownList working correctly when used as an editor in a Kendo UI Grid.
I've got the following #model in my view;
{
"DataItems": [
{ "Id": 1, "Name": "Foo", "Category": { "Id": 1 } },
{ "Id": 2, "Name": "Bar", "Category": { "Id": 2 } }
],
"CategoryOptions": [
{ "Id": 1, "Name": "A" },
{ "Id": 2, "Name": "B" },
{ "Id": 3, "Name": "C" }
],
}
This is passed to my script, which upon initializing constructs the following data source and grid
var gridDataSource = new kendo.data.DataSource({
data: _model.DataItems,
schema: {
model: {
id: "Id",
fields: {
Id: { type: "number", editable: false, nullable: false },
Name: { type: "string", validation: { required: true } },
Category: { type: "object" },
}
},
}
});
$("#grid").kendoGrid({
dataSource: _model.DataItems,
columns: [
{ field: "Id", hidden: true },
{ field: "Name", width: "200px", title: "Name", },
{ field: "Category", title: "Category", editor: categoryDropDownList, template: "#= (Category != null && Category.Name !== null) ? Category.Name : ' ' #" },
{ command: "destroy", title: " "}
],
toolbar: ["save", "cancel"],
editable: true,
});
As you'll notice this grid is in-line editable, so clicking a cell will allow you to edit its contents. To edit Category I've added a custom editor (categoryDropDownList), which displays _model.CategoryOptions.
function categoryDropDownList(container, options) {
$('<input data-value-field="Id" data-text-field="Name" data-bind="value:' + options.field + '"/>').appendTo(container)
.kendoDropDownList({
dataSource: _model.CategoryOptions,
dataValueField: "Id",
dataTextField: "Name",
});
}
This seems to work as expected.
You can click the Category cell, and select a value (A, B, or C). When you remove focus from that cell, a small flag appear in the top left corner to mark that cell as dirty, requiring you to save changes.
In my model the data item Bar has the category B. This means that upon loading the page, one would expect the cell value of Category to be B, as dictated by the template;
#= (Category != null && Category.Name !== null) ? Category.Name : ' ' #
Instead the text value in the Category cell is always empty, as if you hit the else of the ternary if template, which shouldn't be the case. It should be B.
However, if you click the cell to reveal the editor, you'll notice that the selected item in the DropDownList is actually B. Remove focus from the cell, and the value disappears with the DropDownList.
So it's as if the editor knows about the selected category, but the grid doesn't.
Does this make any sense for you guys?
Please leave a comment if you need a better explanation, more code etc.
It's most likely because the editor template is asking for Category.Name, but it is null. The Category object in DataItems only has Id defined and has no idea that there is a relationship defined at CategoryOptions.
In your editor template, you can try something like this (or similar).
#= (Category.Id !== null) ? $.grep(CategoryOptions, function(e){ return e.Id == Category.Id; })[0].Name : ' ' #
Basically, return the Name of the object in CategoryOptions with an Id that matches the Category Id of DataItem.
If trying that does not work, you can try the column.values configuration that kendo supports. I imagine it would look something like this:
Your category column (no more template):
{
field: "Category",
title: "Category",
editor: categoryDropDownList,
values: CategoryOptions
},
Your data model would then need to be like this:
{
"DataItems": [
{ "Id": 1, "Name": "Foo", "Category": 1 },
{ "Id": 2, "Name": "Bar", "Category": 2 }
],
"CategoryOptions": [
{ "value": 1, "text": "A" },
{ "value": 2, "text": "B" },
{ "value": 3, "text": "C" }
],
}
Adding function to kendo template context
Declare your wrapper function inline as part of the editor template:
"# var GetCategoryNameById = function(id){ return $.grep(CategoryOptions, function(e){ return e.Id == id; })[0].Name; }; # #= GetCategoryNameById(name) #"
Kendo Template Hash Usage FYI:
#= # --> Render as HTML
# # --> Arbitrary JS

Getting most basic Backgrid.js example working

I am trying to get the most basic example of backgrid.js to work. In other words, an example where i can drop the source folder into my xampp/htdocs folder and run without having to do anything else.
I have tried many ways to get the code to run but i cannot get anything to show up.
Here is the html page i made to try to see an example working.
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="bootstrap/css/bootstrap.css"/>
<link rel="stylesheet" href="lib/backgrid.css"/>
<script src="jquery-1.10.2.min.js"></script>
<script src="underscore-min.js"></script>
<script src="backbone-min.js"></script>
<script src="lib/backgrid.js"></script>
</head>
<body>
<div id="grid">
<script type="text/javascript">
var Territory = Backbone.Model.extend({});
var Territories = Backbone.Collection.extend({
model: Territory,
url: "territories.json"
});
var territories = new Territories();
var columns = [{
name: "id", // The key of the model attribute
label: "ID", // The name to display in the header
editable: false, // By default every cell in a column is editable, but *ID* shouldn't be
// Defines a cell type, and ID is displayed as an integer without the ',' separating 1000s.
cell: Backgrid.IntegerCell.extend({
orderSeparator: ''
})
}, {
name: "name",
label: "Name",
// The cell type can be a reference of a Backgrid.Cell subclass, any Backgrid.Cell subclass instances like *id* above, or a string
cell: "string" // This is converted to "StringCell" and a corresponding class in the Backgrid package namespace is looked up
}, {
name: "pop",
label: "Population",
cell: "integer" // An integer cell is a number cell that displays humanized integers
}, {
name: "percentage",
label: "% of World Population",
cell: "number" // A cell type for floating point value, defaults to have a precision 2 decimal numbers
}, {
name: "date",
label: "Date",
cell: "date"
}, {
name: "url",
label: "URL",
cell: "uri" // Renders the value in an HTML anchor element
}];
// Initialize a new Grid instance
var grid = new Backgrid.Grid({
columns: columns,
collection: territories
});
// Render the grid and attach the root to your HTML document
$("#example-1-result").append(grid.render().el);
// Fetch some countries from the url
territories.fetch({reset: true});
</script>
</div>
</body>
</html>
Thanks for your time!
You seem to be adding the grid to non-existing element:
$("#example-1-result").append(grid.render().el);
Use $("#grid") instead and you should see the result.

Kendo ListView search doesn't work properly with filtered data

I have a datasource with a filter applied to it. When I enable filterable search for the listview, it wipes out the original filter on the datasource. How can I get it to search WITHIN the filtered data subset?
Here is the issue in action: http://jsfiddle.net/KS7dB/. It is filtered by {b: "2B"}. Start entering "ds" in the search and it wipes out the filter and starts searching everything instead of only the filtered subset. Any idea on how to fix this behavior?
var ds1 = new kendo.data.DataSource({
data: [{
stagename: "ds1 A",
b: "1b"
}, {
stagename: "ds1 B",
b: "2b"
}, {
stagename: "ds1 C",
b: "2b"
}, {
stagename: "ds1 D",
b: "2c"
}, {
stagename: "ds1 E",
b: "2c"
}],
filter: {
field: 'b',
operator: 'eq',
value: '2b'
}
});
$("#stages_listview").kendoMobileListView({
dataSource: ds1,
template: $("#stages_listview_template1").html(),
filterable: {
field: 'stagename',
operator: 'contains',
ignoreCase: true
}
});
I did spent some time digging into this and the problem is that as soon as you create filter on your listview, in practice that is a filter on the underlining data source, they are not two separate cumulative filters . Therefore behaviour you observed seems to be correct.
Work around options:
override the filter function on the data source so it takes argument
passed to it via listview and always append the default data source
filter. Something alongside these lines. I have to admit I didn't
manage to craft final and functional solution.
lw.dataSource.filter = function() {
arguments[arguments.length]= { field: "b", operator: "eq", value: "2b" };
arguments.length += 1;
var result = lw.dataSource.originalfilter.apply(this, arguments);
return result;
}
filter server side

Categories