KendoUI – How to create controls at runtime based on user input?-Collection of common programming errors
Is there a way to create different type of controls in a KendoUI grid column at runtime? The scenario is, I have 2 columns in my grid. The first column displays a dropdown list which has the some strings like, ‘Name’, ‘StartDate’, etc. When user selects a value from it, I want to show an appropriate control in the 2nd column. If the user picks ‘StartDate’, I want to show the ‘DateTime’ control. Can you please let me know how to do this thru ASP.Net MVC 5 wrapper?
Sample Code:
@(Html.Kendo().Grid()
.Name("Grid")
.Columns(columns =>
{
columns.ForeignKey(f => f.FilterName, (System.Collections.IEnumerable)ViewData["Filters"], "Name", "Name");
columns.Bound(f => f.FilterValue).ClientTemplate("#= gridValueColumnFormatter(data) #");
columns.Template(t => { }).HeaderTemplate("").ClientTemplate(@"
remove")
.Width(100).Title("");
})
.ToolBar(toolbar =>
{
toolbar.Create();
toolbar.Save();
})
.Editable(editable => editable.Mode(GridEditMode.InCell))
.Editable(e => e.DisplayDeleteConfirmation(false))
.Sortable()
.Scrollable()
.Events(e => e.Edit("onEdit"))
.DataSource(dataSource => dataSource
.Ajax()
.ServerOperation(false)
.Model(model =>
{
model.Id(f => f.Id);
model.Field(f => f.Id).Editable(false);
})
.Create(create => create.Action("Filter_Create", "Home"))
.Destroy(destroy => destroy.Action("Filter_Delete", "Home"))
.Read(read => read.Action("Filter_Read", "Home"))
.Update(update => update.Action("Filter_Update", "Home"))
)
)
function gridValueColumnFormatter(dataRow) {
var returnValue = "";
if (dataRow.FilterName == "DateTimeRange") {
returnValue = kendo.format('{0:MM/dd/yyyy hh:mm}', new Date(dataRow.FilterValue));
}
else {
returnValue = dataRow.FilterValue;
}
return returnValue;
}
function onEdit(e) {
var actElement = document.activeElement;
//alert(txt.id);
if (actElement.id == "FilterValue") {
var selectedValue = e.model.FilterValue;
var selectedDate = Date.parse(selectedValue);
console.log(selectedValue);
console.log(selectedDate);
if (e.model.FilterName == "DateTimeRange") {
if (isNaN(selectedDate)) {
initDatePicker(new Date());
} else {
//initDatePicker(selectedValue);
$("#FilterValue").kendoDateTimePicker({
value: new Date(selectedValue)
});
}
} else {
if (!isNaN(selectedDate)) {
$("#FilterValue").val("");
}
}
}
}
function initDatePicker(dateValue) {
$("#FilterValue").empty();
$("#FilterValue").kendoDateTimePicker({
value: new Date(dateValue),
format: "MM/dd/yyyy hh:mm",
parseFormats: "MM/dd/yyyy hh:mm"
});
var dateTimePicker = $("#FilterValue").data("kendoDateTimePicker");
dateTimePicker.value(dateValue);
}
function disposeDatePicker() {
var datepicker = $("#FilterValue").data("kendoDateTimePicker");
if (datepicker) {
popup = datepicker.dateView.popup;
element = popup.wrapper[0] ? popup.wrapper : popup.element;
//Move the shared calendar to the body
kendo.ui.DatePicker.sharedCalendar.element.hide().appendTo(document.body);
//remove popup element;
element.remove();
//unwrap element
var input = datepicker.element.show();
input.removeClass("k-input"); //.css("width", "auto");
input.insertBefore(datepicker.wrapper);
datepicker.wrapper.remove();
//remove DatePicker object
input.removeData("kendoDateTimePicker");
}
}
function deleteRow(element) {
grid = $("#Grid").data("kendoGrid");
grid.removeRow($(element).closest("tr"));
}
function createRow() {
grid = $("#Grid").data("kendoGrid");
grid.addRow();
}
Controller:
public class HomeController : Controller
{
public static List ListFilter = new List();
public static List ListUserFilters = new List();
public void Seed()
{
ListFilter = new List{
new ArulUI.Models.Filter { Name = "Name", Type="string"},
new ArulUI.Models.Filter { Name = "Dept", Type="string"},
new ArulUI.Models.Filter { Name = "Age", Type="string"},
new ArulUI.Models.Filter { Name = "DateTimeRange", Type="datetime"}
};
ListUserFilters = new List {
new UserFilter { Id = 1, FilterName="Name", FilterValue = "Empty"},
new UserFilter { Id = 2, FilterName="Dept", FilterValue = "Empty2"},
//new UserFilter { Id = 2, FilterName="DateTimeRange", FilterValue = "12/20/2013 10:00"}
};
}
public ActionResult Index()
{
this.Seed();
ViewData["Filters"] = ListFilter.Select(f => new
{
Name = f.Name,
Type = f.Type
});
return View();
}
public ActionResult Filter_Read([DataSourceRequest] DataSourceRequest request)
{
return Json(ListUserFilters.ToDataSourceResult(request));
}
public ActionResult Filter_Delete([DataSourceRequest] DataSourceRequest request, UserFilter userFilter)
{
if (userFilter != null && ModelState.IsValid)
{
var target = ListUserFilters.Where(f => f.Id == userFilter.Id).FirstOrDefault();
if (target != null)
{
ListUserFilters.Remove(target);
}
}
return Json(ModelState.ToDataSourceResult());
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Filter_Update([DataSourceRequest] DataSourceRequest request, UserFilter userFilter)
{
if (userFilter != null && ModelState.IsValid)
{
var target = ListUserFilters.Where(f => f.Id == userFilter.Id).FirstOrDefault();
if (target != null)
{
int targetIndex = ListUserFilters.IndexOf(target);
ListUserFilters[targetIndex].FilterName = target.FilterName;
ListUserFilters[targetIndex].FilterValue = target.FilterValue;
}
}
return Json(ModelState.ToDataSourceResult());
}
public ActionResult Filter_Create([DataSourceRequest] DataSourceRequest request, UserFilter userFilter)
{
userFilter.Id = ListUserFilters[ListUserFilters.Count - 1].Id + 1;
userFilter.Id = 10;
ListUserFilters.Add(userFilter);
return Json(new[] { userFilter }.ToDataSourceResult(request, ModelState));
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
Thank you
-
You’ll likely have to implement that yourself, e.g. by binding the dropdown’s change event to store the selected control type (e.g. “kendoDatePicker”) in a variable somewhere, then have a data structure that gives you the appropriate options for each control type. Then you can make your editor template for the second column dynamic based on that control type variable.
If you use inline editing and you want to replace the editor directly, your dropdown change handler might look something like this (note that in order to display the various value types nicely, you’ll probably need a complex display template as well):
change: function (e) { // select other column by index, for example var secondColumn = $(e.sender.element).closest("td").siblings().eq(0); var name = $(secondColumn).find("input").attr("name"); secondColumn.empty(); // remove existing editor (you should also call .destroy() for existing widgets in there!) var model = grid._modelForContainer(secondColumn); // get associated data model from kendoGrid instance $("").appendTo(secondColumn).kendoDatePicker(); kendo.bind(secondColumn, model); // bind the model again manually }
See demo here
If you use in-cell editing, then you should use the editor template as I suggested before; the template function for your dynamic column might look something like this:
editor: function (container, options) { // model.typeTitle is set by the dropdown column if (options.model.typeTitle === "DateTime") { $("") .appendTo(container).kendoDateTimePicker(controlOptions["kendoDateTimePicker"]); } else if (options.model.typeTitle === "String") { $("") .appendTo(container); } else if (options.model.typeTitle === "Number") { $("") .appendTo(container).kendoNumericTextBox(); } }
Demo here