ASP.NET MVC: Knockout, JsSite - редактирование комплексных типов или DbLookUp на AJAX
Сайтостроение | создано: 11.07.2013 | опубликовано: 12.07.2013 | обновлено: 13.01.2024 | просмотров: 7964 | всего комментариев: 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)
Сколько раз кликнул на окно выбора, столько дивов и появилось в конце разметки.
Так и было задумано?