I have recently had an issue with Kendo MVC where I have needed to drag a product code from one Kendo Grid into another where the cell value is empty.
Scenario:
Grid A, products ordered, where the vendor sending the list of products doesnt have a full product list and 90% accurate descriptions.
Grid B, Products, with full \ correct descriptions and product codes.
I now need to populate Cell x in Grid A, with data from Cell Y in Grid B. In the current windows (WinForms) application, the user can use drag and drop.
Issue:
Kendo Grids do not easily provide a drag and drop feature.
Kendo themselves, admittedly a while back say this is not supported but produced a fiddle that would allow the dragging and dropping of a cell to re-order the row, and thats about it.
They also, never produced any scenario for ASP.Net MVC.
As this is something that I need, and also appears to be something that others may have searched for, you will see below the code in here to help someone who was \ is in my position, and a fiddle for the Kendo UI, and the MVC examples.
Because of how my project is organised and is using Typescript the MVC version isnt going to be 1:1 exact but will be close enough for someone to follow.
A few caveats on this is when you pick up the item, you can click anywhere on the row. (if anyone can refine this please post an answer, I will test and if it works I will upvote and also incorporate your answer with working code.
In addition to the above the dataItem that you pick up is picked up in a position relative to where the mouse is. This I will fix over time, but if anyone gets to this before me please feel free as above.
First,
Kendo UI Code
Html
<html>
<head>
<title>KendoUI Test Page</title>
<link href="//kendo.cdn.telerik.com/2018.3.1017/styles/kendo.common.min.css" rel="stylesheet" />
<script src="//code.jquery.com/jquery-1.8.2.min.js"></script>
<script src="//kendo.cdn.telerik.com/2018.3.1017/js/kendo.all.min.js"></script>
</head>
<body>
<div id="grid"></div>
<div id="grid2"></div>
</body>
</html>
CSS
.hint {
padding: 7px 10px;
background-color: #FFFFFF;
}
**JavaScript \ JQuery **
var data = [
{ id: 1, text: "text 1", position: 0 },
{ id: 2, text: "text 2", position: 1 },
{ id: 3, text: "text 3", position: 2 }
]
var data2 = [
{ id: 4, text: "", position: 0 },
{ id: 5, text: "", position: 1 },
{ id: 6, text: "", position: 2 }
]
var dataSource = new kendo.data.DataSource({
data: data,
schema: {
model: {
id: "id",
fields: {
id: { type: "number" },
text: { type: "string" },
position: { type: "number" }
}
}
}
});
var dataSource2 = new kendo.data.DataSource({
data: data2,
schema: {
model: {
id: "id",
fields: {
id: { type: "number" },
text: { type: "string" },
position: { type: "number" }
}
}
}
});
var grid = $("#grid").kendoGrid({
dataSource: dataSource,
scrollable: false,
columns: ["id", "text"]
}).data("kendoGrid");
var grid2 = $("#grid2").kendoGrid({
dataSource: dataSource2,
scrollable: false,
columns: ["id", "text"]
}).data("kendoGrid");
grid.table.kendoDraggable({
filter: "tbody > tr",
group: "gridGroup",
threshold: 100,
hint: function(e) {
var dataItem = grid.dataItem(e);
return $('<div class="hint">' + dataItem.text + '</div>').css({ marginLeft: e.clientX, marginTop: e.clientY });
}
});
grid2.table.kendoDropTarget({
group: "gridGroup",
drop: function(e) {
e.draggable.hint.hide();
var dest = $(document.elementFromPoint(e.clientX, e.clientY));
var row = dest.closest('tr')
var uid = row[0].dataset.uid
var originalVal = dest[0].innerHTML
var target = dataSource2.getByUid(uid)
var g = $("#grid2").data("kendoGrid")
$.each(g.dataSource.data(), function(idx, gridrow){
if(gridrow.uid === uid){
var dataItem = g.dataSource.get(gridrow.id)
dataItem.set("text", e.draggable.hint[0].innerHTML);
}
})
}
});
Fiddle
https://jsfiddle.net/SimonPrice/t2aju3c6/77/
MVC 5
Razor Partial
<div class="row">
<div id="divOrderedLines" class="col-md-6 col-sm-6 col-xs-6" hidden>
<div class="panel panel-default">
<div class="panel-heading">OrderedLines</div>
<div class="panel-body">
#Html.Partial("_orderedLines")
</div>
</div>
</div>
<div id="divProductLines" class="col-md-12 col-sm-12 col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">Product Lines</div>
<div class="panel-body">
#Html.Partial("_productLines")
</div>
</div>
</div>
</div>
Ordered Lines \ Dropping \ Droppable Grid
#(Html.Kendo().Grid<zzzViewModel>
()
.Name("epsGrid")
.Columns(columns =>
{
//Columns removed
columns.Bound(c => c.ProductCode).HtmlAttributes(new { #class = "drop-target" });
})
.Events(evt => evt.DataBound("fe_rxManager.SetEpsTableOptions"))
.Events(evt => evt.Change("fe_rxManager.styleColumn"))
.Pageable(pageable => pageable
.Refresh(true)
.PageSizes(true)
.ButtonCount(5)
)
.ToolBar(toolbar =>
{
toolbar.Template(#<text><button id="btnNewOrder" class="btn btn-default" disabled="disabled">New Order <i class="fa fa-plus"></i></button></text>);
})
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action("Ordered_Read", "RxManager"))
.PageSize(20)
)
)
Product Lines \ Draggable Grid
#(Html.Kendo().Grid<xxxViewModel>
()
.Name("rxGrid")
.Columns(columns =>
{
columns.Bound(c => c.OrderId).Visible(false);
columns.Bound(c => c.LineID).Visible(false);
columns.Bound(c => c.ProductCode).HtmlAttributes(new { #class= "product-code" });
columns.Bound(c => c.Quantity);
columns.Bound(c => c.CPQuantity);
columns.Bound(c => c.PQuantity);
columns.Bound(c => c.Description);
columns.Bound(c => c.OnHandQuantity);
})
.Events(evt => evt.DataBound("fe_rxManager.rxLinesDataChanged"))
.Pageable(pageable => pageable
.Refresh(true)
.PageSizes(true)
.ButtonCount(5)
)
.Editable(m => m.Mode(GridEditMode.InCell).DisplayDeleteConfirmation(false))
//.BindTo(#Model.xxxLines)
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action("Product_Read", "RxManager").Data("fe_rxManager.xxxLines_Read_AdditionalData"))
.Model(model =>
{
model.Id(l => l.xxxLineID);
model.Field(p => p.ProductCode).Editable(false);
model.Field(p => p.Description).Editable(false);
model.Field(p => p.Quantity).Editable(false);
model.Field(p => p.CPQuantity).Editable(false);
model.Field(p => p.PQuantity).Editable(true);
model.Field(p => p.PQuantityPrice).Editable(false);
model.Field(p => p.OnHandQuantity).Editable(false);
})
.PageSize(20)
))
Typscript \ JavaScript \ JQuery
SetEpsTableOptions = (e: any) => {
this.dragAndDrop();
this.hideLastColumn(); // Dont worry about this
this.styleColumn(e); // Dont worry about this
}
dragAndDrop = () => {
var rxGrid = $("#rxGrid").data("kendoGrid") as any;
rxGrid.table.kendoDraggable({
filter: "tbody > tr",
group: "gridGroup",
threshold: 100,
hint(e) {
var dataItem = rxGrid.dataItem(e);
return $('<div class="hint">' + dataItem.ProductCode + '</div>').css({ marginLeft: e.clientX, marginTop: e.clientY });
}
});
var epsGrid = $("#epsGrid").data("kendoGrid") as any;
epsGrid.table.kendoDropTarget({
group: "gridGroup",
drop(e) {
e.draggable.hint.hide();
var dest = $(document.elementFromPoint(e.clientX, e.clientY));
var row = dest.closest('tr');
var uid = row[0].dataset.uid;
$.each(epsGrid.dataSource.data(),
(idx, gridrow) => {
if (gridrow.uid === uid) {
var dataItem = epsGrid.dataSource.get(gridrow.id);
dataItem.set("ProductCode", e.draggable.hint[0].innerHTML);
}
});
}
});
}
Hopefully this post can help a few people out. Please feel free to leave positive comments that could either help improve this post.
I have view
#{
ViewBag.Title = "Page Title";
}
#(Html.Kendo().Window()
.Name("activate-window")
.Title("Car selection")
.Content ( #<h5>cars:</h5>+
#Html.Kendo().AutoComplete()
.HtmlAttributes(new { style = "width:100%" })
.Name("vehicle")
.DataTextField("Name")
.DataSource(ds =>
{
ds.Read(r =>
{
r.Action("GetVehicleList", "Home").Data("getVehicle");
}).ServerFiltering(true);
})
.Filter("contains")
.MinLength(3).ToHtmlString() +
"<h5>Device:</h5>"+
#Html.Kendo().MultiSelect()
.Name("device")
.DataTextField("Name")
.DataValueField("Id")
.AutoBind(false)
.DataSource(ds =>
{
ds.Read(r =>
{
r.Action("GetDevice", "Home").Data("getDevice");
}).ServerFiltering(true);
})
.Filter("contains")
.MinLength(3).ToHtmlString()+
"<div>"+
#Html.Kendo().Button()
.Name("close")
.Content("Close")
.Events(e=>e.Click("close")).ToHtmlString()+
"</div>"
)
.Animation(a => a.Open(e => e.Fade(FadeDirection.In)))
.Resizable()
.Draggable()
.Modal(true)
.Visible(false)
.Width(300)
)
#(Html.Kendo().Splitter()
.Name("mainPanel")
.Orientation(SplitterOrientation.Horizontal)
.Panes(panes =>
{
panes.Add()
.HtmlAttributes(new {id = "left-side"})
.Scrollable(true)
.Collapsible(false)
.Size("250px")
.Content(Html.Kendo().Button()
.Name("activate")
.Content("Show window")
.Events(e=>e.Click("activateDevice"))
.ToHtmlString()
);
panes.Add()
.HtmlAttributes(new {id = "right-side"})
.Scrollable(true)
.Collapsible(false)
.Content(Html.Kendo().TabStrip()
.Name("tabStrip")
.Animation(a => a.Open(e => e.Fade(FadeDirection.In)))
.Items(item =>
{
item.Add().Text("Events")
.Selected(true)
.Content(
Html.Kendo().Grid<string>()
.Name("events-grid").ToHtmlString()
);
}).ToHtmlString()
);
})
)
<script>
function activateDevice(e) {
var window = $("#activate-window");
window.data("kendoWindow").center();
window.data("kendoWindow").open();
}
function getVehicle() {
return {
text: $("#vehicle").val()
};
}
function getDevice() {
return {
text: $("#device").data("kendoMultiSelect").input.val()
}
}
function close(e) {
$(this).closest(".k-window-content").data("kendoWindow").close();
}
</script>
when window open all work ok. When I press close button in window header window closed and all ok. But when I press my close button I get exception in
Unhandled exception at line 25, column 6830 in http://localhost:22061/Scripts/kendo/2016.1.112/kendo.all.min.js
0x800a01b6
Object does not support property or method "call"
What I do wrong?
I'm using a kendo treeview as a drop target for my grid rows.
This code is giving me the following error when the row is dropped in the treeview: Uncaught TypeError: item.level is not a function.
How can I solve this issue?
<%=Html.Kendo().TreeView()
.Name("treeview")
.Checkboxes(checkboxes => checkboxes
.Name("checkedActivities")
.CheckChildren(true)
)
.LoadOnDemand(true)
.DataTextField("descriptionText")
.DataSource(dataSource => dataSource
.Read(read => read.Action("Activity", "Activity"))
)
.DragAndDrop(true)
%>
<%: Html.Kendo().Grid<Web.Models.Model>()
.Name("grid")
.BindTo((IEnumerable<Web.Models.Model>)ViewBag.List)
.Columns(columns =>
{
columns.Bound(p => p.PK).Title("PK").Hidden(true);
columns.Bound(p => p.CODE).Title("Code");
columns.Bound(p => p.DESCRIPTION).Title("Description");
})
.DataSource(dataSource => dataSource
.Ajax()
.Model(Model => Model.Id(p => p.PK))
)
%>
<script>
$("#grid").kendoDraggable({
filter: "tr",
hint: function (element) {
return element.clone().css({
"opacity": 0.6,
"background-color": "#0cf"
});
}
});
$("#treeview").kendoDropTarget({
drop: function (e) {
console.log("drop");
var dataSourceGrid = $("#grid").data("kendoGrid").dataSource;
var dataSourcetreeview = $("#treeview").data("kendoTreeView").dataSource;
var draggableElement = e.draggable.currentTarget,
dataItem = dataSourceGrid.getByUid(draggableElement.data("uid"));
if (dataItem == undefined) {
dataItem = dataSourcetreeview.getByUid(draggableElement.data("uid"));
}
else {
dataSourcetreeview.add({ descriptionText: dataItem["DESCRIPTION"] });
}
}
});
</script>
I'm trying to display a pop up window when a user clicks on a button but I cant seem to get this to work. I can fire the event when using a custom command but I need a textbox and button next to each other in the same column.
Any ideas why this isn't working?
#(Html.Kendo().Grid(Model)
.Name("gridPopup")
.Columns(columns =>
{
columns.Bound(p => p.ProductName).Width(120);
columns.Template(#<text></text>).Title("").Width(110).ClientTemplate(
Html.Kendo().TextBox()
.Name("search_textfield")
.Value("").ToClientTemplate().ToString()
+ " " +
Html.Kendo().Button()
.Name("search_button")
.HtmlAttributes(new { type = "button", #class = "k-primary" })
.Events(e =>
e.Click("SearchButton")
)
.Content("...").ToClientTemplate().ToString()
);
})
.Editable(editable => editable.Mode(GridEditMode.InCell))
.Pageable()
.Scrollable()
.HtmlAttributes(new { style = "height:320px;" })
.DataSource(dataSource => dataSource
.Ajax()
.Batch(true)
.PageSize(20)
.ServerOperation(false)
.Events(events => events.Error("errorHandler"))
.Model(model =>
{
model.Id(p => p.ProductID);
model.Field(p => p.ProductID).Editable(true);
model.Field(p => p.CategoryID).DefaultValue(1);
})
.Read(read => read.Action("ForeignKeyColumn_Read", "Home"))
.Update(update => update.Action("ForeignKeyColumn_Update", "Home"))
.Create(create => create.Action("ForeignKeyColumn_Create", "Home"))
.Destroy(destroy => destroy.Action("ForeignKeyColumn_Destroy", "Home"))
)
)
<script type="text/javascript">
function errorHandler(e) {
if (e.errors) {
var message = "Errors:\n";
$.each(e.errors, function (key, value) {
if ('errors' in value) {
$.each(value.errors, function () {
message += this + "\n";
});
}
});
alert(message);
}
}
function SearchButton(e)
{
alert("Search Button Clicked");
}
</script>
Weird! .Events(e => e.Click("SearchButton")) is not generating the onclick attribute if used inside a ClientTemplate()
Try this, it will work for you, but i will wait for someone to explain it :)
.HtmlAttributes(new { type = "button", #class = "k-primary", onclick = "SearchButton()" })
Edit:
The explanation that I found is here: How do I use a Kendo UI widget inside a Grid client column template?. It says script tags are not automatically evaluated inside a Grid client column template, so any included widgets will not be initialized.
Therefore initializing the nested controls in .Events(e => e.DataBound("onDatabound"))
function onDatabound()
{
jQuery("#gridPopup tbody [class='k-primary']").each(function () {
$(this).kendoButton({ "click": SearchButton });
})
}
i m using Kendo UI apsnet and i have a Gird with autocomptele as template in 3rd column and i want send data using javascript function "onAutoCompleteX", in this function i want to get id of active autocomplete to send text as parametere to action "GetArticle" but my probleme is how get this id, tried many methods always i get "undefined" or error
#using KendoUIMvcApplication2.Areas.Gescom.Models.Achat
<style>
.k-widget .templateCell
{
overflow: visible;
}
</style>
<script>
function initMenus(e) {
$(".templateCell").each(function () {
eval($(this).children("script").last().html());
});
}
function onAutoCompleteSelectX(e) {
var dataItem = this.dataItem(e.item.index());
var url = '#Url.Action("GetArticle", "Fiche")';
$.ajax({
url: url,
data: { code: dataItem.Code }, //parameters go here in object literal form
type: 'GET',
datatype: 'json',
success: function (data) {
if (data == null)
document.getElementById('labelx').innerHTML = "null";
else
document.getElementById('labelx').innerHTML = data.Code;
},
error: function () {
document.getElementById('labelx').innerHTML = "error";
}
});
}
function onAutoCompleteX() {
var currentId = $(this).attr('id');
//var currentId = $(this).id;
//document.getElementById('labelx').innerHTML = $(this).className; //$obj.attr('id');
return {
text: document.getElementById(currentId).value
//text: $("#id_of_another_autocomplete").val() works fine when i set static id manually
};
}
</script>
<div class="lines-tab-doc">
#(Html.Kendo().Grid<LineAppelOffre>()
.Name("grid-lines-doc")
// Declare grid column
.Columns(columns =>
{
// Cretae all the columns base on Model
columns.Bound(l => l.Document);
columns.Bound(l => l.LigneOriginale);
columns.Template(l => { }).Title(#Resources.Resource.Article)
.HtmlAttributes(new { #class = "templateCell" })
.ClientTemplate(
Html.Kendo().AutoComplete()
.Name("Article")
.HtmlAttributes(new { id = "#=LigneOriginale#", style = "width:100%;" })
.DataTextField("Code")
.Filter(FilterType.Contains)
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetArticles", "Fiche").Data("onAutoCompleteX");
})
.ServerFiltering(true);
})
.Events(e => { e.Select("onAutoCompleteSelectX"); }).ToClientTemplate().ToHtmlString()
);
columns.Bound(l => l.Fournisseur);
columns.Bound(l => l.RefArtFrs);
// Edit and Delete button column
columns.Command(command =>
{
command.Edit();
command.Destroy();
}).Width(200);
})
.Events(ev => ev.DataBound("initMenus"))
// Declare ajax datasource.
// CRUD operation are wired back to ASP MVC Controller/Action e.g. HomeController, GetAll
// Set the model Id
.DataSource(datasoure => datasoure.Ajax()
.Model(model =>
{
//model.Id(l => l.Document);
model.Id(l => l.LigneOriginale);
})
.Read(read => read.Action("LinesAppelOffre_Read", "Achat"))
.Create(create => create.Action("LinesAppelOffre_Add", "Achat"))
.Update(update => update.Action("LinesAppelOffre_Update", "Achat"))
.Destroy(delete => delete.Action("LinesAppelOffre_Delete", "Achat"))
.PageSize(10)
)
// Add tool bar with Create button
.ToolBar(toolbar => toolbar.Create())
// Set grid editable.
.Editable(editable => editable.Mode(GridEditMode.InCell))
.Scrollable(scr=>scr.Height(327))
.Sortable()
.Selectable()
.Navigatable()
.Pageable(pageable =>
{
pageable.Refresh(true);
pageable.PageSizes(true);
pageable.Messages(msg => msg.Empty(null));
})
)
</div>
You can get your AutoComplete id like that:
function onAutoCompleteX(e) {
var currentId = e.sender.element.attr('id');
...
}
But I'm not sure if you have explicity set name as "Article" .Name("Article") you will not always get "Artilcle" as id, even if you set it using .HtmlAttributes property.
If so, just try to use different attribute then id and get is same way.
i used document.activeElement
Browser compatibility
Chrome 2
Firefox (Gecko) 3.0
Internet Explorer 4
Opera 9.6
Safari 4.0