【问题标题】:KendoUI - How to create controls at runtime based on user input?KendoUI - 如何在运行时根据用户输入创建控件?
【发布时间】:2014-01-03 00:42:50
【问题描述】:

有没有办法在运行时在 KendoUI 网格列中创建不同类型的控件?场景是,我的网格中有 2 列。第一列显示一个下拉列表,其中包含一些字符串,例如“名称”、“开始日期”等。当用户从中选择一个值时,我想在第二列中显示一个适当的控件。如果用户选择“StartDate”,我想显示“DateTime”控件。你能告诉我如何通过 ASP.Net MVC 5 包装器来做到这一点吗?

示例代码:

@(Html.Kendo().Grid<ArulUI.Models.UserFilter>()
    .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(@"
            <a href='javascript: void(0)' class='abutton delete' onclick='deleteRow(this)' title='remove'>remove</a>")
             .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"))
    )
)

<script>

    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();
    }
</script>

Controller:

public class HomeController : Controller
    {
        public static List<ArulUI.Models.Filter> ListFilter = new List<ArulUI.Models.Filter>();
        public static List<UserFilter> ListUserFilters = new List<UserFilter>();
        public void Seed()
        {
            ListFilter = new List<Models.Filter>{
                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<UserFilter> {
                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();
        }
    }

谢谢

【问题讨论】:

    标签: javascript kendo-ui kendo-grid kendo-asp.net-mvc


    【解决方案1】:

    您可能必须自己实现,例如通过绑定Dropdown的change event将所选控制类型存储在某个位置的变量中(例如“kendodatepicker”),然后具有数据结构,为每个控件类型提供适当的选项。然后,您可以根据该控件类型变量使第二列的编辑器模板动态化。

    如果您使用内联编辑并且想要直接替换编辑器,您的下拉更改处理程序可能看起来像这样(请注意,为了很好地显示各种值类型,您可能还需要一个复杂的显示模板):

    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
    
        $("<input data-bind='value: " + name + "'/>").appendTo(secondColumn).kendoDatePicker();
        kendo.bind(secondColumn, model); // bind the model again manually
    }
    

    demo here

    如果您使用单元内编辑,那么您应该使用我之前建议的编辑器模板;动态列的模板函数可能如下所示:

    editor: function (container, options) {
        // model.typeTitle is set by the dropdown column
        if (options.model.typeTitle === "DateTime") {
            $("<input data-bind='value:name' />")
                .appendTo(container).kendoDateTimePicker(controlOptions["kendoDateTimePicker"]);
        } else if (options.model.typeTitle === "String") {
            $("<input data-bind='value:name' />")
                .appendTo(container);
        } else if (options.model.typeTitle === "Number") {
            $("<input data-bind='value:name' />")
                .appendTo(container).kendoNumericTextBox();
        }  
    }
    

    Demo here

    【讨论】:

    • 我尝试了你提到的一些东西。但我不知道如何替换第二列的编辑控件。我在问题中添加了代码。你能帮忙吗?
    • 谢谢拉尔斯。我尝试了您的建议,尽管它创建了控件,但所选值并未更新到源。我设法以其他方式[更新了问题中的代码]做了同样的事情,但有两个问题。 1. DateTimePicker 框是空白的,它总是在展开时显示今天的日期。 2. 导航月份时会短暂显示 2 个日历。如果您可以查看我的代码并让我知道我缺少什么,我将不胜感激。非常感谢。
    • @sarav 再次更新(我没有使用你的代码,但希望你能从我的解决方案中调整它)
    • 非常感谢 Lars 的帮助。我尝试使用您的代码,但是当我将编辑模式更改为“incell”时它不起作用。我更新了 JSfiddle (jsfiddle.net/42BWh/1)。你能看看吗?谢谢。
    • @sarav 你不应该使用编辑事件,而是你的专栏的编辑器模板;我更新了帖子
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-22
    • 2021-11-18
    • 2017-09-11
    • 2014-01-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多