KendoUI - Как создавать элементы управления во время выполнения на основе пользовательского ввода?

Есть ли способ создать различные типы элементов управления в столбце сетки 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();
        }
    }

Спасибо


person sarav    schedule 03.01.2014    source источник


Ответы (1)


Вам, вероятно, придется реализовать это самостоятельно, например. путем привязки события изменения раскрывающегося списка для сохранения выбранного типа элемента управления (например, " 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
}

См. демонстрацию здесь

Если вы используете редактирование в ячейке, вам следует использовать шаблон редактора, как я предлагал ранее; функция шаблона для вашего динамического столбца может выглядеть примерно так:

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

Демо здесь

person Lars Höppner    schedule 03.01.2014
comment
Я пробовал что-то вроде того, что вы отметили. Но я не знаю, как заменить элемент управления редактированием 2-го столбца. Я добавил код в вопрос. Не могли бы вы помочь? - person sarav; 03.01.2014
comment
Спасибо, Ларс. Я попробовал ваше предложение, и хотя он создает элемент управления, выбранное значение не обновляется до источника. Мне удалось сделать то же самое другим способом [обновленный код в вопросе], но есть 2 проблемы. 1. Поле DateTimePicker пусто и всегда показывает сегодняшнюю дату при расширении. 2. Кратко показывает 2 календаря при навигации по месяцам. Я ценю, если вы можете взглянуть на мой код и сообщить мне, что мне не хватает. Большое тебе спасибо. - person sarav; 04.01.2014
comment
@sarav снова обновлен (я не использовал ваш код, но надеюсь, что вы сможете адаптировать его из моего решения) - person Lars Höppner; 04.01.2014
comment
Я очень ценю вашу помощь здесь, Ларс. Я пробовал с вашим кодом, но он не работает, когда я меняю режим редактирования на «incell». Я обновил JSfiddle (jsfiddle.net/42BWh/1). Не могли бы вы взглянуть? Спасибо. - person sarav; 04.01.2014
comment
@sarav вам следует использовать для этого не событие редактирования, а шаблон редактора вашей колонки; я обновил пост - person Lars Höppner; 04.01.2014
comment
Спасибо, Ларс :). Я ценю это. Однако вопрос: знаете ли вы, как сделать такой же шаблон динамического редактирования в ASP.Net MVC? Поскольку вся модель недоступна для шаблона редактирования, я не могу проверить «typeTitle» внутри шаблона. - person sarav; 06.01.2014
comment
Я не знаю, как это сделать, и, к сожалению, не могу разобраться в этом прямо сейчас — я полагаю, вы могли бы попытаться просто написать код JS (блок if) в своем шаблоне редактора (т. е. не использовать гибкий синтаксис в все там); если это не сработает, я бы предложил задать здесь отдельный вопрос о том, как преобразовать шаблон JS в шаблон MVC (или обратиться в службу поддержки Telerik) - person Lars Höppner; 07.01.2014
comment
другой вариант (хотя и не очень красивый): используйте событие DataBound сетки, чтобы заменить шаблон редактора (т.е. прокрутите grid.options.columns и установите column.editor = function (container, options) ... для соответствующего имени столбца) - person Lars Höppner; 07.01.2014
comment
Спасибо, Ларс. Я хотел добиться того же в ListView. Задал еще один вопрос здесь. Не могли бы вы сообщить мне, если у вас есть какие-либо идеи? - person sarav; 09.01.2014