ASP.NET MVC: Knockout, JsSite - редактирование комплексных типов или DbLookUp на AJAX
Сайтостроение | создано: 11.07.2013 | опубликовано: 12.07.2013 | обновлено: 13.01.2024 | просмотров: 7692 | всего комментариев: 2
Тема статьи тривиальна: “Выборка (подстановка) комплексных типов при редактировании данных”. Конечно же речь идет об использовании Knockoutjs и, соответственно, AJAX. Применять в редактировании буду опять же библиотеку скриптов JsSite.
А что в итоге?
Для того, чтобы сразу же стало понятно, ради чего мы сегодня собрались в этом маленьком блоге, позвольте показать вам видеоролик, который наглядно демонстрирует результат реализации поставленной задачи.
Редактирование комплексных типов при помощи Knockout
В конце статьи вы можете скачать демонстрационный проект и провести свои эксперименты.
Постановка задачи
Есть две сущности “Пользователь” и “Питомец” (Person и Pet). У пользователя может быть один питомец. Требуется возможность выбирать домашнего питомца у пользователя при редактировании. Отображение и подстановка питомца должны быть в виде объекта. Контрол для отображение должен иметь подменяемые шаблоны.
Немного классов на JavaJscript
Если учесть, что сущности имеют зависимость “один-ко-многим”, то классы моделей в JavaScript могут выглядеть таким образом:
$(function () { "use strict"; site.m.Pet = function (dto) { var me = this, data = dto || {}; me.id = ko.observable(data.id); me.name = ko.observable(data.name); me.type = ko.observable(data.type); me.selected = ko.observable(false); me.petName = ko.computed(function () { if (me.name && me.type) { return me.type() + ' ' + me.name(); } return ''; }); return me; }; site.m.Person = function (dto) { var me = this, data = dto || {}; me.firstName = ko.observable(data.firstName); me.lastName = ko.observable(data.lastName); me.pet = ko.observable(data.pet); me.selected = ko.observable(false); me.dirtyFlag = new ko.DirtyFlag([ me.firstName, me.lastName ]); return me; }; });
Обратите внимание на строки 11 и 28 – это свойство требуется для контрола DataSource (именно его я и буду использовать). Подробное описание самого контрола, его принципов работы, свойств и методов можно посмотреть в описании (скоро).
А в строках 30-34 я подключил возможность отслеживания состояния полей на предмет изменения. Надеюсь в следующих статьях я затрону и эту тему.
Тестовые данные для классов
Наполнение данных я вынесу в отдельный не только класс, но и в отдельный файл. Наполним наши классы некоторым количеством данных:
site.utils.dataPets = [ new site.m.Pet({ 'id': 51, 'name': 'Пупсик', 'type': 'Кот' }), new site.m.Pet({ 'id': 52, 'name': 'Семен', 'type': 'Конь' }), new site.m.Pet({ 'id': 53, 'name': 'Мурка', 'type': 'Хорек' }), new site.m.Pet({ 'id': 54, 'name': 'Золотуха', 'type': 'Рыбка' }) ]; site.utils.dataPeople = [ new site.m.Person( { 'firstName': 'Иван', 'lastName': 'Прохоров', 'pet': site.utils.dataPets[2] } ), new site.m.Person( { 'firstName': 'Игорь', 'lastName': 'Пупкин', 'pet': site.utils.dataPets[1] } ), new site.m.Person( { 'firstName': 'Александр', 'lastName': 'Болотов', 'pet': site.utils.dataPets[0] } ) ];
Пришло время создавать ViewModel для главной (стартовая) страницы.
$(function () { "use strict"; site.vm.viewModel = function () { var meta = site.utils.metaForDemo, clock = new site.controls.Clock(), dsPets = new site.controls.DataSource({ items: site.utils.dataPets }), dsPeople = new site.controls.DataSource({ items: site.utils.dataPeople, events: { selectedHandler: function (item) { selectedItem(item); } } }), selectedItem = ko.observable(), select = function (item) { selectedItem(item); }; return { meta: meta, select: select, selectedItem: selectedItem, clock: clock, dsPeople: dsPeople, //pets: pets, dsPets: dsPets }; }(); ko.applyBindings(site.vm.viewModel); });
Теперь немного HTML-разметки:
@{ ViewBag.Title = "Контрол подброра"; } <h2 data-bind="text: meta.title, click: function () { window.location = meta.helplink(); }, attr: { 'style': 'cursor:pointer' }"></h2> <p data-bind="text: meta.description"></p> <div class="row"> <div class="span6"> <h3>Список "Person"</h3> <ul data-bind="template: { 'name': 'person-template.view', foreach: dsPeople.items }"></ul> </div> <div class="span6"> <h3>Список "Pets"</h3> <ul data-bind="template: { 'name': 'pet-template.view', foreach: dsPets.items }"></ul> </div> </div> <!-- ko if: selectedItem --> <div class="row"> <div class="span12"> <h3>Выбранный пользователь</h3> <div data-bind="template: { 'name': 'person-template.view', 'data': selectedItem }"></div> </div> <div class="span6"> <h3>Первый способ</h3> <div data-bind="template: { 'name': 'person-template.edit', data: selectedItem }"></div> </div> <div class="span6"> <h3>Второй способ</h3> <div data-bind="template: { 'name': 'person-template.edit2', data: selectedItem }"></div> </div> </div> <!-- /ko --> @section scripts { <script src="~/Scripts/app/dump.js"></script> <script src="/scripts/app/site.homeIndex.js"></script> } @*<div data-bind="dump: dsPets.items"></div>*@ <div class="clock" data-bind="text: clock.time"></div>
Строки 5-7 выводят заголовок (header), на который можно кликнуть, чтобы перейти на наш блок. Это сделано лишь для примера связывания (binding), и не несет никакой смысловой нагрузки (Олег, надеюсь ты понял как можно использовать function в разметке knockout для связывания).
Строка 14: Выводим все записи из DataSource “Person” (dsPerson) при помощи шаблона (template), который берется из отдельного файла.
Примечание: все шаблоны загружаются из удаленных шаблонов при помощи специального модуля External Template Engine. Шаблоны я положил в папку Templates. Настройка (указание путей и всё такое) модуля происходит в файле site.core.js.
И что же мы видим?
После некоторых манипуляций с буквами английского алфавита, у меня появилось некоторое количество файлов с классами, сервисами и другой всякой фигней. Теперь можно запустить проект и посмотреть что получилось. Итак, у нас есть список пользователей:
Также на странице отображается список домашних питомцев:
Если мы выберем одного из пользователей, то он отобразится ниже. А также станут доступны для редактирования данные этого пользователя. Для того чтобы вы смогли сравнить, я сделал это двумя способами. Первый способ “стандартный” при выборка происходит при помощи html-контрола <select>.
А вот второй способ “продвинутый” – уже использует DataSource, DBLookup и шаблоны для отображения.
Остановлюсь немного подробнее на контроле DbLookUp. Контрол имеет несколько шаблонов:
- Шаблон FieldTemplate (не обязательный). Этот шаблон отвечает за отображение самого контрола (например, поле label и кнопка с точками, так как показан на картинке) при просмотре формы, по умолчанию если не задан шаблон, используется именно это отображение.
- Шаблон ModalTemplate (не обязательный). Шаблон для отображения модально окна. На видео хорошо видно, что используется Twitter Bootstrap. Вы можете использовать CSS Framework для вывода модального окна.
- Шаблон DsTemplate (обязательный). Этот шаблон используется для отображения списка.
Нажимаем на кнопку вызова диалога.
Выбираем новое значение, нажимаем [выбрать] и, ву-а-ля! Новое значение “падает”
В качестве заключения
В качестве заключения хочу привести вашему вниманию некоторое количество ссылок:
- Knockoutjs фрэймворк на javascript, обеспечивающий MVVM паттерн на HTML.
- JsSite nuget-пакет, который содержит некоторое количество файлов (скриптов), использующие knockout для построения приложений в стиле SPA (Single Page Application).
- Twitter Bootstrap фрэймворк на CSS+JavaScript, который существенно облегчает разработку дизайна, в том числе одним из плюсов которого является возможность замены тем (themes).
Следующая статья будет о том самом контроле, который называется DataSource, и о том как можно просто из без особых усилий вывести данные с Web API на странице. На этом хочу закончить и сказать “спасибо за внимание”.
Комментарии к статье (2)
Сколько раз кликнул на окно выбора, столько дивов и появилось в конце разметки.
Так и было задумано?