I require a Kendo Grid for the user to enter 'n' number of results, then click a button (seperate from the grid control) which will take all the results added to the grid and save them to the database. This should be a simple task considering there are no CRUD operations going on with the grid itself except to add additional, blank rows for data entry...
HOWEVER,
The problem is that the content of the grid is not static and can vary in column size (from 1 to 6) based on user input (my example shows how this will be represented in the form of an array Lots). It seems that most if not all tutorials available seem to focus solely on static content with very little help when it comes to anything else.
So far (with some feedback from Telerik) I have come up with the following...
Set up a Lot Schema i.e. a placeholder for all the data for each Lot in the array:
var Lots = [];
Lots.push({ ID: 13, LotNumber: "158UL" }),
Lots.push({ ID: 14, LotNumber: "646UE" });
var LotResultsSchema = [];
for (var p = 0; p < Lots.length; ++p) {
LotResultsSchema.push({
Result: {
Count: '',
Mean: '',
SD: ''
}
});
}
Set up the overall grid Schema Model ID:
var schemaModel = kendo.data.Model.define({
id: "ID",
fields: {
ID: { editable: false, nullable: true },
ResultDateTime: {
type: "date", validation: {
required: true
}
},
LotResults: LotResultsSchema,
StandardComment: {
ID: {
nullable: true, validation: {
required: false
}
},
Description: {
defaultValue: "<empty>",
validation: {
required: false
}
}
},
ReviewComment: {
ID: {
nullable: true, validation: {
required: false
}
},
Description: {
defaultValue: "<empty>",
validation: {
required: false
}
}
}
}
});
Set up the datasource for the grid based on the schema above:
var gridConfigDataSourceAdd = new kendo.data.DataSource({
data: [],
schema: {
model: schemaModel
}
});
Set up the column schema (again taking into account that there can be multiple columns created based on array size):
var columnSchema = [];
columnSchemaAdd.push({ field: "ResultDateTime", format: "{0:yyyy/MM/dd hh:mm:ss}", editor: dateTimeEditor, title: 'Date Time' });
for (var j = 0; j < Lots.length; ++j) {
columnSchemaAdd.push({
title: Lots[j].LotNumber,
field: Lots[j].ID,
columns: [{
field: "LotResults[" + j + "].Result.Count",
title: 'Count'
}, {
field: "LotResults[" + j + "].Result.Mean",
title: 'Mean'
}, {
field: "LotResults[" + j + "].Result.SD",
title: 'SD'
}]
});
}
columnSchemaAdd.push({ field: "StandardComment", title: 'Comment', editor: standardCommentDropDownEditor, template: "#=StandardComment.Description#" });
columnSchemaAdd.push({ field: "ReviewComment", title: 'Review Comment', editor: reviewCommentDropDownEditor, template: "#=ReviewComment.Description#" });
columnSchemaAdd.push({ command: ["edit", "destroy"] });
Set up the overall grid:
$("#configAddGrid").kendoGrid({
dataSource: gridConfigDataSourceAdd,
height: 550,
navigatable: true,
autoBind: false,
editable: {
mode: "inline"
},
toolbar: ["create"],
columns: columnSchemaAdd
});
Running this code and clicking the 'Add New' button to create a new row produces the following error:
Uncaught TypeError: Cannot read property 'Result' of undefined
I understand why I am getting this error, due to the new item being created with undefined LotResults. What I can't understand is how this can happen when the default values are being set up in the Lot Schema.
Any advice is appreciated, I am hoping someone has used Kendo Grids for the same purpose before as I would really like to get a look at an example which works!
I think the problem is with LotResultsSchema. Instead of creating it as a separate array, can try to merge it to the fields class?
<script>
var Lots = [];
Lots.push({ ID: 13, LotNumber: "158UL" }),
Lots.push({ ID: 14, LotNumber: "646UE" });
var fields1 = {
ID: { editable: false, nullable: true },
ResultDateTime: {
type: "date", validation: {
required: true
}
},
StandardComment: {
ID: {
nullable: true, validation: {
required: false
}
},
Description: {
defaultValue: "<empty>",
validation: {
required: false
}
}
},
ReviewComment: {
ID: {
nullable: true, validation: {
required: false
}
},
Description: {
defaultValue: "<empty>",
validation: {
required: false
}
}
}
};
for (var p = 0; p < Lots.length; ++p) {
fields1['Count' + Lots[p].ID] = {type : "number"};
fields1['Mean'+Lots[p].ID] = {type : "number"};
fields1['SD' +Lots[p].ID] = {type : "number"};
}
var schemaModel = kendo.data.Model.define({
id: "ID",
fields: fields1
});
var gridConfigDataSourceAdd = new kendo.data.DataSource({
data: [],
schema: {
model: schemaModel
}
});
var columnSchemaAdd = [];
columnSchemaAdd.push({ field: "ResultDateTime", format: "{0:yyyy/MM/dd hh:mm:ss}", title: 'Date Time' });
for (var j = 0; j < Lots.length; ++j) {
columnSchemaAdd.push({
title: Lots[j].LotNumber,
field: Lots[j].ID,
columns: [{
field: 'Count' + Lots[j].ID ,
title: 'Count'
},
{
field: 'Mean'+Lots[j].ID ,
title: 'Mean'
}, {
field: 'SD' + Lots[j].ID ,
title: 'SD'
}]
});
}
columnSchemaAdd.push({ field: "StandardComment", title: 'Comment', template: "#=StandardComment.Description#" });
columnSchemaAdd.push({ field: "ReviewComment", title: 'Review Comment', template: "#=ReviewComment.Description#" });
columnSchemaAdd.push({ command: ["edit", "destroy"] });
$("#configAddGrid").kendoGrid({
dataSource: gridConfigDataSourceAdd,
height: 550,
navigatable: true,
autoBind: false,
editable: {
mode: "inline"
},
toolbar: ["create"],
columns: columnSchemaAdd
});
</script>
A sample http://dojo.telerik.com/uHucA
Related
I am trying to get the value from the ID field on the expanded row from the Master grid in order to populate the corresponding field on the Details grid when adding a new record in the Details grid. I tried the following:
$scope.detailGridOptions = function (dataItem) {
return {
dataSource: {
type: 'aspnetmvc-ajax',
transport: {
read: "/Home/GetTaggedContractorsByMajor"
,
create: "/Home/AddContractorToTag"
},
serverPaging: true,
serverSorting: true,
serverFiltering: true,
schema: {
data: "Data",
total: "Total",
model: {
id: "ContractorID",
fields: {
CompanyID: { editable: true, nullable: true, type: "string" },
TagID: { editable: true, nullable: true, type: "number" },
vchCompanyName: { editable: false, nullable: false, type: "string"},
Date: { editable: false, nullable: false, type: "date" },
AddedBy: { editable: false, nullable: false, type: "string" }
}
}
},
pageSize: 5,
filter: { field: "TagID", operator: "eq", value: dataItem.TagID }
},
scrollable: false,
sortable: true,
pageable: true,
editable: "popup",
edit: function (e) {
var uid = $(this.element[0]).closest('.k-detail-row').prev().attr('data-uid');
var selector = kendo.format("tr[data-uid='{0}']", uid);
var currentGridElement = $(selector).closest("[data-role=grid]");
var parentRow = currentGridElement.closest(".k-detail-row").prev();
var parentGrid = parentRow.closest("[data-role=grid]").data("kendoGrid");
var parentModel = parentGrid.dataItem(parentRow);
return {
TagID: parentModel.TagID
};
},
toolbar: ["create"],
columns: [
{
field: "TagID"
, hidden: true
}
,
{
field: "CompanyID",
title: "CompanyID"
, editor: companyDropDownEditor
}
,
{
field: "vchCompanyName",
title: "Company Name"
}
,
{
field: "Date",
title: "Date Added"
}
,
{
field: "AddedBy",
title: "Added By"
}
]
};
};
"parentGrid" variable is always null. However, if use the name of the grid directily as $scope.tagsGrid, I can get the grid but the line var parentModel = $scope.tagsGrid.dataItem(parentRow); gives the error "Unable to get property 'TagID' of undefined or null reference". I think even if I am able to get that part of the code working, I am still missing something as trying to manually set the ID like this
return {
TagID: 1
};
does not set the ID either. If it is not possible to automatically set the ID field for the new detail row, is it possible to get the ID value from Master and send it as a parameter with my create Url?
Any assistance is greatly appreciated!
I was able to get this to work with a few tweaks to the edit event function:
edit: function (e) {
var grid = $scope.tagsGrid;
var uid = e.model.uid;
var selector = kendo.format("tr[data-uid='{0}']", uid);
var currentGridElement = $(selector).closest("[data-role=grid]");
var parentRow = currentGridElement.closest(".k-detail-row").prev();
var parentModel = grid.dataItem(parentRow);
e.model.TagID = parentModel.TagID;
},
I'm getting array of properties and I want to show all of them in grid.
How to do it? Is it possible?
This is my code:
function StartBuild(ProfileProperties) {
var details = [];
for (i = 0; i < ProfileProperties.length; i++) {
details[i]=[{ name: ProfileProperties[i][0], email: ProfileProperties[i][1], phoneWork: ProfileProperties[i][2], Mobile: ProfileProperties[i][3], ManagerName: ProfileProperties[i][4] }];
}
$(document).ready(function () {
var datasource = new kendo.data.DataSource({
transport: {
type:"odata",
read: function (e) {
e.success(details);
},
pageSize: 10,
batch: false,
schema: {
model: {
fields: {
name: { editable: false },
Fname: { editable: false }
}
}
}
}
});
$("#grid").kendoGrid({
dataSource: datasource,
pegable: true,
sortable: {
mode: "single",
allowUnsort: false
},
columns: [{
field: "name",
title: "name",
filterable: {
cell: {
enabled:true
}
}
}, {//[Bild, nameP, email,phonework, Mobile, ManagerName]
field: "email",
title: "email"
}, {
field: "phoneWork",
title: "phoneWork"
}, {
field: "Mobile",
title: "Mobile"
}, {
field: "Manager",
title: "Manager"
}],
filterable: {
mode: "row"
},
});
});
}
Only if I write details[0] it shows the first properties otherwise it doesn't show anything.
It looks like there are a couple of problems.
http://dojo.telerik.com/#Stephen/uVOjAk
First, the transport setup is incorrect. pageSize, batch, and schema are not part of the transport configuration...you need to take them out of the transport block and just have them in the datasource block.
You want:
var datasource = new kendo.data.DataSource({
transport: {
type:"odata",
read: function (e) {
e.success(details);
}
},
pageSize: 10,
batch: false,
schema: {
model: {
fields: {
name: { editable: false },
Fname: { editable: false }
}
}
}
});
Instead of(what you have):
var datasource = new kendo.data.DataSource({
transport: {
type:"odata",
read: function (e) {
e.success(details);
},
pageSize: 10,
batch: false,
schema: {
model: {
fields: {
name: { editable: false },
Fname: { editable: false }
}
}
}
}
});
Second, the details needs to be an array of objects, not an array of an array of an object.
You want:
var details = [];
for (i = 0; i < ProfileProperties.length; i++) {
details.push({ name: ProfileProperties[i][0], email: ProfileProperties[i][1], phoneWork: ProfileProperties[i][2], Mobile: ProfileProperties[i][3], ManagerName: ProfileProperties[i][4] });
}
Instead of:
var details = [];
for (i = 0; i < ProfileProperties.length; i++) {
details[i]=[{ name: ProfileProperties[i][0], email: ProfileProperties[i][1], phoneWork: ProfileProperties[i][2], Mobile: ProfileProperties[i][3], ManagerName: ProfileProperties[i][4] }];
}
Two other issues that are not directly causing a problem, but are incorrect are:
Your dataSource.schema configuration does not match your actual data at all. The schema really needs to match the fields of the actual data otherwise it cannot match up the configuration.
You spelled "pageable" wrong in the grid configuration.
I wish to create a grid that each row has grid inside it.
Both grids need to be editable and I managed to do so. However, when I try to add a new row to the outer grid, all the data inside it gone.
You can find the demo here: http://dojo.telerik.com/UqURE
Can you please help with this issue?
Thanks!
var outerDataSource= new kendo.data.DataSource({
schema: {
model: {
field1: { type: "string", validation: { required: true } },
field2: { type: "boolean", validation: { required: true } },
field3: { type: "string", validation: { required: true } }
}
}
});
$("#outerGrid").kendoGrid({
dataSource: outerDataSource,
detailInit: onExpansion,
toolbar: ["create"],
columns: [
{ field: "field1", title: "field1" },
{ field: "field2", title: "field2" },
{ field: "field3", title: "field3" },
{ command: ["destroy"], title: " " }],
editable: true
});
function onExpansion(e) {
var insideDataSource= new kendo.data.DataSource({
schema: {
model: {
in1: { type: "string", validation: { required: true } },
in2: { type: "string", validation: { required: true } }
}
},
data: [{
in1: "Click to edit",
in2: "Click to edit"
}]
});
var headers = $("<div/>").appendTo(e.detailCell).kendoGrid({
dataSource: insideDataSource,
width: 90,
toolbar: ["create"],
editable: true,
columns: [
{ field: "in1" },
{ field: "in2" },
{ command: ["destroy"], title: " " }]
});
};
Answer from Telerik:
Please note that operations like paging, sorting, filtering and
editing cause the Grid to rebind and reevaluate all templates inside
it (including the details). That why in order to preserve the child
Grid data between rebinds you should either save it to remove service
(link to documentation here) or add it as navigation property
collection to the parent Grid model (demo is available here).
http://www.telerik.com/forums/hirarchialy-editable-grids
I have a grid where each row has a select drop down box in with values.
When an item is selected, how can I make the grid reload to reflect the change?
The reason I want to do this is because the item selected from the drop down effects an amount in another column.
Here is the code for my dropdown:
function shippingModelSelect(container, options)
{
$('<input required data-text-field="modelName" data-value-field="modelId" data-bind="value:' + options.field + '"/>')
.appendTo(container)
.kendoDropDownList({
autoBind: false,
dataSource: [
{
"modelName": "Individual shipping cost",
"modelId": 1
},
{
"modelName": "Based on weight",
"modelId": 2
},
{
"modelName": "Based on value",
"modelId": 3
}
],
close: function()
{
handleChange();
}
});
}
My handle change function:
var gridAlert = $('#gridAlert');
var handleChange = function(input, value) {
if(input && value)
{
detail = 'Product <b>'+input[0].name+'</b> changed to <b>'+value+'</b>';
gridAlert.html('<b>Changes saved!</b><p>'+detail+'</p>');
gridAlert.fadeIn('slow', function(){
setTimeout(function(){
gridAlert.fadeOut();
}, 2000)
});
}
dataSource.sync();
}
And finally my grid setup:
dataSource = new kendo.data.DataSource({
serverPaging: true,
serverSorting: true,
serverFiltering: true,
serverGrouping: true,
serverAggregates: true,
transport: {
read: {
url: ROOT+'products/manage'
},
update: {
url: ROOT+'products/set-product',
type: "POST",
data: function(data)
{
data.dateAdded = kendo.toString(data.dateAdded, 'yyyy-MM-dd hh:ii:ss')
return data;
}
}
},
pageSize: 20,
sort: {
field: 'id',
dir: 'desc'
},
error: function(e) {
alert(e.errorThrown+"\n"+e.status+"\n"+e.xhr.responseText) ;
},
schema: {
data: "data",
total: "rowcount",
model: {
id: "id",
fields: {
id: {
type: 'number',
editable: false
},
SKU: {
type: "string"
},
name: {
type: "string"
},
dateAdded: {
type: "date",
format: "{0:yyyy/MM/dd hh:mm}",
editable: false
},
shippingModel: {
type: "string"
},
shippingModelId: {
type: "number"
},
price: {
type: "number"
}
}
}
}
})
$('#productGrid').kendoGrid({
dataSource: dataSource,
autoBind: true,
columns: [
{
field: "image",
title: "Image",
width: 30,
template: "#= '<img title=\"'+alt+'\" src=\"images/'+image+'\"/>' #"
},
{
field: "SKU",
title: "SKU",
width: 50,
headerTemplate: "<abbr title=\"Stock Keeping Unit - A unique identifier for this product\">SKU</abbr>"
},
{
field: "stockQuantity",
title: "Quantity",
width: 30
},
{
field: "name",
title: "Name",
width: 200
},
{
field: "dateAdded",
title: "Date Added",
width: 80,
template: "#= niceDate #"
},
{
field: "shippingModelId",
title: "Shipping Model",
width: 50,
editor: shippingModelSelect,
template: "#= shippingModel #"
},
{
field: "price",
title: "Price",
width: 80,
//format: "{0:c}",
template: "#= '£'+price.toFixed(2)+'<br /><span class=\"grey\">+£'+shipping+' shipping</span>' #"
}
],
sortable: true,
editable: true,
pageable: {
refresh: true,
pageSizes: [10, 20, 50]
},
scrollable: false,
reorderable: true,
edit: function(e) {
var value;
var numericInput = e.container.find("[data-type='number']");
// Handle numeric
if (numericInput.length > 0)
{
var numeric = numericInput.data("kendoNumericTextBox");
numeric.bind("change", function(e) {
value = this.value();
handleChange(numericInput, value);
});
return;
}
var input = e.container.find(":input");
// Handle checkbox
if (input.is(":checkbox"))
{
value = input.is(":checked");
input.change(function(){
value = input.is(":checked");
handleChange(input, value);
});
}
else
{
// Handle text
value = input.val();
input.keyup(function(){
value = input.val();
});
input.change(function(){
value = input.val();
});
input.blur(function(){
handleChange(input, value);
});
}
}
}
)
You will need to do two things.
Wait for changes to finish syncing
Tell the grid to re-read the datasource
This should do both for you
dataSource.bind("sync", function(e) {
$('#productGrid').data("kendoGrid").dataSource.read();
});
For more info see the datasource sync event and datasource read method on their docs site.
I'm currently implementing a kendo grid and i'm populating it with local data.
That is; I have generated a JSON string from my action and supplying that string on the view page.
In the end i would like to know if it is possible to implement full CRUD functions with local data?
here's a sample of the code written so far;
<div id="example" class="k-content">
<div id="grid"></div>
<script>
$(document).ready(function() {
var myData = ${coursemodules},
dataSource = new kendo.data.DataSource({
data: myData,
batch: true,
pageSize: 30,
schema: {
model: {
id: "id",
fields: {
id: { editable: false, nullable: true},
name: { type: "string", validation: { required: true }},
qualificationLevel: { type: "string", validation: { required: true }},
description: { type: "string", validation: { required: true }},
published: { type: "boolean" },
gateApprove: { type: "boolean" },
duration: { type: "number", validation: { min: 1, required: true } },
academicBody: { type: "string" }
}
}
}
});
$("#grid").kendoGrid({
dataSource: dataSource,
height: 350,
scrollable: true,
sortable: true,
pageable: true,
toolbar: ["create", "save", "cancel"],
columns: [
{
field: "id",
title: "ID",
width: '3%'
},
{
field: "name",
title: "Course Title",
width: '20%'
},
{
field: "description",
title:"Description",
width: '35%'
},
{
field: "published",
title: "Published",
width: '7%'
},
{
field: "gateApprove",
title: "Gate Approve",
width: '7%'
},
{
field: "duration",
title: "Duration",
width: '5%'
},
{
field: "academicBody.shortName",
title: "Academic Body",
width: '10%'
}
],
editable: true
});
});
</script>
</div>
I have realise that for the datasource, you have to declare transport to implement the CRUD. However, I need to declare "data". I tried declaring both transport and data. That doesn't seem to work.
Yes you can Here is JSFiddle Hope this will help you.
// this should be updated when new entries are added, updated or deleted
var data =
[{
"ID": 3,
"TopMenuId": 2,
"Title": "Cashier",
"Link": "www.fake123.com"},
{
"ID": 4,
"TopMenuId": 2,
"Title": "Deposit",
"Link": "www.fake123.com"}
];
$("#grid").kendoGrid({
dataSource: {
transport: {
read: function(options) {
options.success(data);
},
create: function(options) {
alert(data.length);
},
update: function(options) {
alert("Update");
},
destroy: function(options) {
alert("Destroy");
alert(data.length);
}
},
batch: true,
pageSize: 4,
schema: {
model: {
id: "ID",
fields: {
ID: {
editable: false,
nullable: true
},
TopMenuId: {
editable: false,
nullable: true
},
Title: {
editable: true,
validation: {
required: true
}
},
Link: {
editable: true
}
}
},
data: "",
total: function(result) {
result = result.data || result;
return result.length || 0;
}
}
},
editable: true,
toolbar: ["create", "save", "cancel"],
height: 250,
scrollable: true,
sortable: true,
filterable: false,
pageable: true,
columns: [
{
field: "TopMenuId",
title: "Menu Id"},
{
field: "Title",
title: "Title"},
{
field: "Link",
title: "Link"},
{
command: "destroy"}
]
});
When binding local data, the Grid widget utilizes an abstraction that represents a local transport. Therefore, it does not require a custom transport; modifications made in the grid are reflected to the bound data source. This includes rollbacks through a cancellation.
There is fully functional example of this in telerik documentation
What you need is define transport block in dataSource object with custom CRUD funtions which update local source.
transport: {
create: function(options){
var localData = JSON.parse(localStorage["grid_data"]);
options.data.ID = localData[localData.length-1].ID + 1;
localData.push(options.data);
localStorage["grid_data"] = JSON.stringify(localData);
options.success(options.data);
},
read: function(options){
var localData = JSON.parse(localStorage["grid_data"]);
options.success(localData);
},
update: function(options){
var localData = JSON.parse(localStorage["grid_data"]);
for(var i=0; i<localData.length; i++){
if(localData[i].ID == options.data.ID){
localData[i].Value = options.data.Value;
}
}
localStorage["grid_data"] = JSON.stringify(localData);
options.success(options.data);
},
destroy: function(options){
var localData = JSON.parse(localStorage["grid_data"]);
for(var i=0; i<localData.length; i++){
if(localData[i].ID === options.data.ID){
localData.splice(i,1);
break;
}
}
localStorage["grid_data"] = JSON.stringify(localData);
options.success(localData);
},
}