Editable Detail Template Kendo UI Grid - razor

Update
I have managed to get the form rendering (see image) with values and editor templates.
Sorry for blacked out labels. NDA requirements...
This now works across multiple rows because I am using the uid of the row to give the detail template a unique name doing this:
#(Html.Kendo().TabStrip()
.Name("Details_#=uid#")
I've updated the source below to the latest version and have included a list of issues that I'd still love help with.
Outstanding issues:
Serializing the detail template along with the grid when I save changes
Assign labels and validation rules using Data Annotations on the View Model (This doesn't seem to work since I can't seem to get Html helpers working. Advice would be great!
Orignal Post text (Source code has been updated)
I am trying to create a Batch Grid where each item contains a detail template.
Each detail template contains a tabstrip that I want to hold extra form data within.
Right now, I have the standard batch grid working, but I can't seem to get the information on screen for each item AND have it be editable. Also, the detail template breaks when more than one row is present. The template still renders, but buttons are non-functional because both templates have the same id understandably breaking the user's ability to interact, but I'm unsure of how to ensure unique identifiers for each row template (maybe use the parent uid somehow?)
I am also unsure of how to serialize this form data once I have completed this first step, but I can make a separate question for that should it prove necessary.
Grid
#(Html.Kendo().Grid(Model.ItemModelList)
.Name("ItemGrid")
.Columns(columns =>
{
//Other columns omitted for brevity
columns.Bound(i => i.Description).Width(100);
columns.Command(command =>
{
command.Destroy();
}).Width(60);
})
.ClientDetailTemplateId("ItemDetails")
.ToolBar(toolbar =>
{
toolbar.Create();
toolbar.Save();
})
.Editable(editable => editable.Mode(GridEditMode.InCell).CreateAt(GridInsertRowPosition.Bottom))
.Pageable()
.Sortable()
.Scrollable()
.Resizable(resize => resize.Columns(true))
.DataSource(dataSource => dataSource
.Ajax()
.Batch(true)
.ServerOperation(false)
.Events(events =>
{
events.Error("ItemGrid_ErrorHandler");
})
.Model(model =>
{
model.Id(i => i.ItemModelID);
model.Field(i => i.DimensionUOM).DefaultValue("in");
model.Field(i => i.WeightUOM).DefaultValue("lbs");
})
.Create(create => create.Action("CreateProducts", "ItemGrid"))
.Read(read => read.Action("GetProducts", "ItemGrid"))
.Update(update => update.Action("UpdateProducts", "ItemGrid"))
.Destroy(destroy => destroy.Action("DeleteProducts", "ItemGrid"))
)
)
Detail Template
<script id="ItemDetails" type="text/kendo-tmpl">
#(Html.Kendo().TabStrip()
.Name("Details_#=uid#")
.SelectedIndex(0)
.Items(items =>
{
items.Add().Text("test").Content(#<div>
<table id="testForm">
<tr>
<td>TEST</td>
</tr>
<tr>
</tr>
</table>
</div>);
items.Add().Text("test2").Content(#<div>
<table id="test2Form">
<tr>
<td><label>A</label></td>
<td><label>B</label></td>
<td><label>C</label></td>
<td><label>D</label></td>
<td><label>E</label></td>
</tr>
<tr>
<td>
<input class="k-textbox" value="#=objectA#">
</td>
<td>
#(Html.Kendo().DropDownList()
.Name("objectB")
.Value("#=objectB#")
.DataTextField("Text")
.DataValueField("Value")
.BindTo(new SelectList((System.Collections.IEnumerable)ViewBag.objectBListing, "Value", "Value"))
.ToClientTemplate()
)
</td>
<td>
#(Html.Kendo().DropDownList()
.Name("objectC")
.Value("#=objectC#")
.DataTextField("Text")
.DataValueField("Value")
.BindTo(new SelectList((System.Collections.IEnumerable)ViewBag.objectCListing, "Value", "Value"))
.ToClientTemplate()
)
</td>
<td><input class="k-textbox" value="#=objectD#"></td>
<td><input class="k-textbox" value="#=objectE#"></td>
</tr>
</table>
</div>);
})
.ToClientTemplate()
)

You should create a model for your extra information and add it as a property of your ItemModelList like this:
public class BaseMode
{
public string UID { get; set; } // Create your own UID, distinguished from Telerik UID by casing.
}
public class ExtraInfoModel : BaseModel
{
[DisplayName("Object A")]
[Required] // For example
public string ObjectA { get; set; }
[DisplayName("Object B")]
[UIHint("DropDownList")]
public int? ObjectB { get; set; }
[DisplayName("Object C")]
public int? ObjectC { get; set; }
public ExtraInfoModel(string uid)
{
this.UID = uid;
}
}
public class ItemModelList : BaseModel
{
public ExtraInfoModel ExtraInfo { get; set; }
public ItemModelList()
{
this.UID = Guid.NewGuid().ToString(); // Not sure about syntax, please review.
this.ExtraInfo = new ExtraInfoModel(this.UID); // Guarantee ExtraInfo.UID is the same as parent UID when you get model from DB.
}
}
Create a partial view for your detail extra info, using a kendo grid in your second tab:
#model ExtraInfoModel
#(Html.Kendo().TabStrip()
.Name("Details_#=UID#")
.SelectedIndex(0)
.Items(items =>
{
items.Add().Text("test").Content(#<text>
<div>
<table id="testForm">
<tr>
<td>TEST</td>
</tr>
<tr></tr>
</table>
</div>
</text>);
items.Add().Text("test2").Content(#<text>
#(Html.Kendo().Grid<ExtraInfoModel>()
.Name("gvDetail_#=UID#")
.Columns(c =>
{
c.Bound(m => m.ObjectA).ClientTemplate(Html.Kendo().TextBox()
.Name("ObjectA")
.HtmlAttributes(new { id = "ObjectA_#=UID#" })
.Value(Model.AgencyCode)
.ToClientTemplate()
.ToHtmlString());
c.Bound(m => m.ObjectB).ClientTemplate(Html.Kendo().DropDownList()
.Name("ObjectB")
.HtmlAttributes(new { id = "ObjectB_#=UID#" })
.Value((Model != null && Model.ObjectB.HasValue) ? Model.ObjectB.ToString() : string.Empty)
.OptionLabel("Select B...")
.BindTo(ViewBag.Data)
.ToClientTemplate()
.ToHtmlString());
// Config ObjectC same as ObjectB.
})
.BindTo(new ExtraInfoModel[] { Model }) // Your detail grid has only one row.
)
</text>);
})
)
In your main view page, using server detail template instead of client detail template. I suggest to use server template because I'm using server binding in step 2. Of course, you can change it to ajax binding or local binding (by defining event OnDetailExpand)
#Html.Kendo().Grid(Model.ItemModelList)
...
.DetailTemplate(#<text>
#Html.Partial("YourPartialName", item.ExtraInfo) // item stands for ItemModelList, which is the binding data item.
</text>)
And the last one, for your first issue, serializing extra info data on save, we should handle change event of each extra info property to set value and dirty flag to master data item. Remember that kendo grid in batch editing only submits dirty data items. Back to step 2:
c.Bound(m => m.ObjectB).ClientTemplate(Html.Kendo().DropDownList()
.Name("ObjectB")
.HtmlAttributes(new { id = "ObjectB_#=UID#" })
.Value((Model != null && Model.ObjectB.HasValue) ? Model.ObjectB.ToString() : string.Empty)
.OptionLabel("Select B...")
.BindTo(ViewBag.Data)
.Events(e => e.Change("onChangeObjectB")) // Added this line
.ToClientTemplate()
.ToHtmlString());
<script type="text/javascript">
function onChangeObjectB(e) {
var selectedValue = this.value();
var sender = e.sender;
if (sender != undefined && sender.length > 0) {
var detailRow = sender.closest(".k-detail-row");
var masterRow = detailRow.prev();
var mainGrid = $("#ItemGrid").data("kendoGrid");
if (mainGrid != null) {
var masterRowIndex = masterRow.parent().find(".k-master-row").index(masterRow);
var dataItem = mainGrid.dataSource._data[masterRowIndex];
dataItem.ObjectB = selectedValue;
dataItem._dirty = true;
}
}
}
</script>
And the save action will work as normal.

Related

Kendo.Grid does not send some data to the controller on "Create" operation

My Kendo.Grid need to perform CRUD operations on the data on SAVE CHANGES click
This is the Grid's definition:
#(Html.Kendo().Grid(Model.TicketReportPropertyList)
.Name("TicketReportPropertyGrid")
.Columns(columns =>
{
columns.Bound(c => c.ID).Hidden();
columns.Bound(c => c.PropertyName).Title("Property Name").EditorTemplateName("_PropertyNameEditor").Width(900);
columns.Bound(c => c.Amount).Title("Amount").Format("{0:C}").Width(90);
columns.Command(c => c.Custom("Delete").Click("DeleteRecord"));
})
.Events(events => events.DataBound("Databound").SaveChanges("SaveGrid").Edit("Edit"))
.ToolBar(toolbar =>
{
toolbar.Create();
toolbar.Save();
})
.Editable(editable => editable.Mode(GridEditMode.InCell).DisplayDeleteConfirmation(false))
.DataSource(dataSource => dataSource
.Ajax()
.Batch(true)
.ServerOperation(false)
.Model(model =>
{
model.Id(c => c.ID);
model.Field(c => c.PropertyName);
model.Field(c => c.Amount);
})
.Events(events =>
{
events.RequestEnd("onRequestEnd");
})
.Read(read => read.Action("GetData", "TicketReportProperty", Model))
.Create(create => create.Action("AddTicketReportProperty", "TicketReportProperty"))
.Update(update => update.Action("UpdateTicketReportProperty", "TicketReportProperty"))
.Destroy(delete => delete.Action("DeleteTicketReportProperty", "TicketReportProperty"))
)
)
When doing the update, the following method of the controller is invoked:
[HttpPost]
public ActionResult UpdateTicketReportProperty([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<TicketReportPropertyEntity> ticketReportPropertyList)
{
TicketReportPropertyModel model = new TicketReportPropertyModel();
model = new TicketReportPropertyModel().UpdateTicketReportProperties(ticketReportPropertyList);
if (!model.Success)
{
ModelState.AddModelError("TicketReportProperty", model.ErrorDescription);
}
return Json(new[] { model.TicketReportPropertyList }.ToDataSourceResult(request, ModelState));
}
When Update is performed, all the data I need is populated inside of ticketReportPropertyList parameter and I can update the database
However, when performing a Create operation, the controller's method got hit, but ticketReportPropertyList is not populated with some of the parameters I need as happen during Update.
When adding new record the following method is invoked:
[HttpPost]
public ActionResult AddTicketReportProperty([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<TicketReportPropertyEntity> ticketReportPropertyList)
{
TicketReportPropertyModel model = new TicketReportPropertyModel();
model = new TicketReportPropertyModel().AddTicketReportProperty(ticketReportPropertyList);
if (!model.Success)
{
ModelState.AddModelError("TicketReportProperty", model.ErrorDescription);
}
return Json(new[] { model.TicketReportPropertyList }.ToDataSourceResult(request, ModelState));
}
This is TicketReportPropertyEntity:
public class TicketReportPropertyEntity
{
public int ID { get; set; }
public int TicketID { get; set; }
public decimal Amount { get; set; }
public string PropertyName { get; set; }
public int ReportPropertyID { get; set; }
}
What am I missing here?
I believe your issue can be fixed by simply replacing:
[Bind(Prefix = "models")]IEnumerable<TicketReportPropertyEntity> ticketReportPropertyList
with
TicketReportPropertyEntity ticketReportPropertyList
You are not sending a list when creating each row, you are simply sending one object.
OK, so for creates kendo initializes a new TicketReportPropertyList and then binds values from your columns. So your missing columns will be null. One mechanism to resolve that is to specify DefaultValue for those columns in your model definition. The value can come from a field in your model, a set value, value of a hidden, etc. So I typically have a model for the page the grid is displayed on with values for added items. Then I can do:
.Model(model =>
{
model.Id(c => c.ID);
model.Id(c => c.TicketID).DefaultValue(Model.TicketID);
model.Id(c => c.ReportPropertyID).DefaultValue(Model.ReportPropertyID);
model.Field(c => c.PropertyName);
model.Field(c => c.Amount);
})
Another way would be to handle the Grid's edit event, check for inserts and set the values:
if (e.model.isNew()) {
model.set("TicketID", $("#TicketID).val()); // hidden
model.set("ReportPropertyID", $("#ReportPropertyID).val()); // hidden
}

Monthpicker in Kendo UI for MVC works but sets current month when changing focus from grid cell

I have set up a "monthpicker" in a cell of a Kendo grid. The picker works fine and the column shows MMMM yyyy (e.g. April 2019)
However, when I move focus from the cell it doesn't set the cell as dirty and reverts back to current month and year.
Editor template (called Month.cshtml)
#model DateTime?
#{string[] formats = { "MMMM yyyy" }; }
#(Html.Kendo().DatePickerFor(m => m)
.Name("monthpicker")
.Start(CalendarView.Year)
.Depth(CalendarView.Year)
.Format("MMMM yyyy")
.DateInput()
.Culture("en-US")
.ParseFormats(formats)
.HtmlAttributes(new { style = "width: 100%", title = "monthpicker" })
)
Model:
[Display(Name = "Month", ResourceType = typeof(Resources.Resources))]
[UIHint("Month")]
public DateTime Month { get; set; }
View
#(Html.Kendo().Grid<GrindrodDataCapture.Models.MonthlyOceanPlan>()
.Name("grid")
.Columns(columns =>
{
columns.Bound(c => c.Month).Format("{0:MMMM yyyy}");
//etc
})
.ToolBar(toolbar =>
{
toolbar.Create();
toolbar.Save();
})
.Editable(editable => editable.Mode(GridEditMode.InCell))
.Pageable()
.Sortable(sortable =>
{
sortable.SortMode(GridSortMode.SingleColumn);
})
.Filterable()
.DataSource(dataSource => dataSource
.Ajax()
.Events(events => events.Error("error_handler"))
.Sort(p => { p.Add("Month").Descending(); })
.Model(model => model.Id(p => p.ID))
.Read(read => read.Action("MonthlyOceanPlans_Read", "MonthlyOceanPlanGrid"))
.Create(create => create.Action("MonthlyOceanPlans_Create", "MonthlyOceanPlanGrid"))
.Update(update => update.Action("MonthlyOceanPlans_Update", "MonthlyOceanPlanGrid"))
.Destroy(destroy => destroy.Action("MonthlyOceanPlans_Destroy", "MonthlyOceanPlanGrid"))
)
I got a support response from Telerik which fixed it :)
"Hi Evan,
I noticed that the name of the date picker editor does not match the name of the field it edits. The editor is bound to its corresponding field of the model using the name setting. Currently the binder will try to bind the editor to the monthpicker field of the model. However, the actual field in the model is called Month.
Furthermore, when using a WidgetFor helper, you can omit the name configuration as the name is automatically set to the name of the field.
Could you pleas remove the Name configuration of the editor and let me know if the editor binds as expected?"

Persist State In Kendo MVC Grid With Custom Command Columns?

Having issues persisting state in mvc grid when using custom command columns. Here is the grid's wrapper
#(Html.Kendo().Grid < Weighmaster_Web.Data.Entity.Destination > ()
.Name("grid")
.Columns(columns => {
columns.Bound(c => c.Description);
columns.Bound(c => c.CODE);
columns.Command(c => {
if (bUpdate) c.Custom("Edit").Click("editItem");
if (bDelete) c.Custom("Delete").Click("deleteItem");
}).Width(175);
})
.Scrollable()
.Groupable()
.Sortable()
.ToolBar(toolbar => {
if (bCreate) {
toolbar.Create().HtmlAttributes(new {
id = "addDestination"
}).Text("Add Destination");
}
})
.ToolBar(t => t.Excel())
.Excel(excel => excel
.FileName("Destinations.xlsx")
.Filterable(true)
.AllPages(true)
.ProxyURL(Url.Action("Excel_Export_Save", "MaterialTransaction"))
)
.Filterable(filterable => filterable.Extra(false))
.Resizable(resize => resize.Columns(true))
.Reorderable(reorder => reorder.Columns(true))
.Pageable(pageable => pageable
.Refresh(true)
.PageSizes(true)
.ButtonCount(5))
.DataSource(dataSource => dataSource
.Ajax()
.Events(events => events.Error("error_handler"))
.Read(read => read.Action("DestinationIndex", "Destination").Type(HttpVerbs.Post))
.Model(model => model.Id(p => p.Id))
.PageSize(20)
.Create(update => update.Action("DestinationSave", "Destination").Type(HttpVerbs.Post)))
)
Here i define a click event handler in the wrapper for both edit and delete buttons. i am using custom commands so that i may define custom edit template.
When you look at the actual jquery for this wrapper , i can see the event handler defined.
Then when you leave the page , this code is ran to save the grid's state in a cookie :
$(window).unload(function () {
var grid = $("#grid").data("kendoGrid");
var dataSource = grid.dataSource;
var state = {
columns: grid.columns,
page: dataSource.page(),
pageSize: dataSource.pageSize(),
sort: dataSource.sort(),
filter: dataSource.filter(),
group: dataSource.group()
};
$.cookie(username + "DestinationGridState", JSON.stringify(state), { expires: 365 });
})
The grid's state is read from cookie in $(document).ready like this :
$(document).ready(function () {
var grid = $("#grid").data("kendoGrid");
var toolbar = $("#grid").find(".k-grid-toolbar").html();
var state = $.cookie(username + "DestinationGridState");
if (state) {
state = JSON.parse(state);
var options = grid.options;
options.columns = state.columns;
options.dataSource.page = state.page;
options.dataSource.pageSize = state.pageSize;
options.dataSource.sort = state.sort;
options.dataSource.filter = state.filter;
options.dataSource.group = state.group;
if (grid) {
grid.destroy();
//grid.wrapper.html("");
}
$("#grid").empty().kendoGrid(options).find(".k-grid-toolbar").html(toolbar);
}
});
After the grid's state is read from the cookie, no click event handler is defined for the custom edit command button. So , i guess my question is; How do i correctly save the state of the grid so that my custom command buttons will retain their event handlers?
As mentioned in the kendo documentation:
JSON.stringify() cannot serialize function references (e.g. event
handlers), so if stringification is used for the retrieved Grid state,
all configuration fields, which represent function references, will be
lost.
I once had the same issue, when I was trying to save the filters values in session. I was doing just like you, but then I realized that I didn't need to restore the columns state. If you remove the row options.columns = state.columns;
the custom command will work just as expected.
Hope it helps.
I had a similar problem. After loading the settings my custom delete button stopped working. This was the solution that I came up with:
Save the original grid options. After parsing the saved settings, restore the original values, in this case the column where my delete buttons was placed.
Hope this helps.
$("#Grid").on("click",
".loadsetting",
function(e) {
var grid = $("#Grid").data("kendoGrid");
var originalOptions = grid.options; // Save original options
e.preventDefault();
var options = localStorage["msettings"];
if (options) {
var parsedOptions = JSON.parse(options);
parsedOptions.toolbar = [
{
template: $("#toolbarTemplate").html()
}
];
// Restore values
parsedOptions.columns[30] = originalOptions.columns[30];
grid.setOptions(parsedOptions);
}
});

Post additional data as list kendo multiselect read

I have a two kendo multiselect and i want my roles multiselect to sort of cascade from the other in a way that on the roles multiselect read i want to post a list of the values selected in my systems multiselect. This is what my roles multiselect looks like:
#(Html.Kendo().MultiSelect()
.Name("Roles")
.DataTextField("Name")
.DataValueField("Id")
.Placeholder("Select roles")
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetRoles", "UserAdmin").Data("additionalItemsGetRoles");
})
.ServerFiltering(true);
})
)
<script>
function additionalItemsGetRoles() {
var multiselect = $("#Systems").data("kendoMultiSelect").dataItems();
var length = multiselect.length;
var systems = [];
for (var i = 0; i < length; i++) {
systems.push({
Name: multiselect[i].Name,
SystemId: multiselect[i].SystemId,
Description: multiselect[i].Description
});
}
var json = JSON.stringify(systems);
console.log(json);
return json;
}
</script>
and here is what my action method looks like:
public ActionResult GetRoles([DataSourceRequest] DataSourceRequest request, IList<SystemViewModel> systems)
{
And here is what my console.log(json) shows in the console.
And here is my viewmodel:
public string SystemId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
I tried to set the action method as [httpPost] but then it can't find the method at all.
Everytime it posts to the controller it get null. What am i doing wrong here?
OK, if I understand correctly, what you want to do is to filter one multiselect list, based on the selected items in another multiselect list. Yes?
I am actually doing something similar, and here is what I did:
First, setup the two MultiSelect "widgets"
Set the Change event on the first MultiSelect ("regionChange")
Set the .Data parameter of DataSource to a js function ("regionfilter")
#(Html.Kendo().MultiSelect()
.Name("region")
.DataTextField("Name")
.DataValueField("Id")
.AutoBind(false)
.Events(e => e.Change("regionChange"))
.DataSource(ds => ds
.Read(read => read.Action("GetRegionsForFilter", "Authorization")
)
)
#(Html.Kendo().MultiSelect()
.Name("branch")
.DataTextField("Name")
.DataValueField("Id")
.AutoBind(false)
.DataSource(ds => ds
.Read(read => read.Action("GetBranchesForFilter", "Authorization")
.Data("regionfilter"))
.ServerFiltering(true)
)
)
Define js functions (I have an additional function to get the MultiSelect values, because I am using this on a couple of other MultiSelect "widgets" no the page, AND I am actually doing reverse filtering for some (such as Branch/Region though I snipped out the filtering being done on region)
function regionChange() {
var value = this.value(),
grid = $("#grid").data("kendoGrid");
<-- snipped logic related to filtering grid -->
$('#branch').data('kendoMultiSelect').dataSource.read();
}
function regionfilter() {
var values = getMultiSelectValues("region");
return { regions: values.toString() };
}
function getMultiSelectValues(multiSelectControl) {
var multiSelect = $("#" + multiSelectControl).data("kendoMultiSelect");
var values = multiSelect.value($("#value").val());
return values;
}
Finally, in my controller I am just returning a JsonResult (get request) that accepts a string argument (comma separated list of strings)
public JsonResult GetBranchesForFilter(string regions)
{
var list = _repository.Branches().GetAll().Select(x => new { x.Id, x.Name, x.RegionId });
if (!String.IsNullOrWhiteSpace(regions))
list = list.Where(x => regions.Contains(x.RegionId.ToString()));
return Json(list.OrderBy(o => o.Name), JsonRequestBehavior.AllowGet);
}

how to get data from JSON without using script?

i want to get data from json file without using script code!
iam using MVC4 and want to put the code in the .cshtml file, how can i do this?
( Iam using kendo function)
example:
#{
ViewBag.Title = "Home Page";
}
<div class="chart-wrapper">
#(Html.Kendo().Chart()
.Name("chart")
.Title(title => title
.Text("Share of Internet Population Growth, 2007 - 2012")
.Position(ChartTitlePosition.Bottom))
.Legend(legend => legend
.Visible(false)
)
.DataSource(dataSource=>dataSource.Read(read=>read.Url("~/")))
.Events(e => e.SeriesClick("onSeriesHover"))
.Series(series => {
series.Pie(new dynamic[] {
new {category="Asia",value=53.8,color="#9de219"},
new {category="Europe",value=16.1,color="#90cc38"},
new {category="LatinAmerica",value=11.3,color="#068c35"},
new {category="Africa",value=9.6,color="#006634"},
new {category="MiddleEast",value=5.2,color="#004d38"},
new {category="NorthAmerica",value=3.6,color="#033939"}
})
.Labels(labels => labels
.Template("#= category #: #= value#%")
.Background("transparent")
.Visible(true)
.Color("Red")
)
.StartAngle(150);
})
.Tooltip(tooltip => tooltip
.Visible(true)
.Format("{0}%")
)
)
<script>
function onSeriesClick(e) {
alert(kendo.format("Series click :: {0} ({1}): {2}",
e.series.name, e.category, e.value));
}
</script>
</div>
i have use
.DataSource(dataSource=>dataSource.Read(read=>read.Url("~/")))
but not working
Try like this,
Example
View
#(Html.Kendo().Chart<Model.DashboardPieChartModel>()
.Name("PieChartPopup")
.Events(events => events.DataBound("onDataBound"))
.Legend(legend => legend
.Visible(false)
)
.DataSource(ds =>
{
ds.Read(read => read.Action("Read_PieChart", "Dashboard"));
}
)
.Series(series =>
{
series.Pie(
model => model.Percentage,
model => model.Service, null, null
).Labels(labels => labels
.Visible(true)
.Template("${ category } - ${ value }%")
).Overlay(ChartPieSeriesOverlay.None);
})
.Tooltip(tooltip => tooltip
.Visible(true)
.Template("${ category } - ${ value }%")
)
)
Controller
public JsonResult Read_PieChart()
{
//Whatever you do here
return Json(return your data);
}
Read this link: http://demos.kendoui.com/dataviz/pie-charts/remote-data.html