Mvc mvvm на html або використання knockout при створенні сайту (частина 1 з 2)
Що таке Knockout?
Якщо говорити про Knockout, то я не буду вдаватися в подробиці, а просто перерахую чотири основні принципи фреймворка описані на офіційному сайті:
- Декларативне зв'язування (declarative binding);
- Автоматичне оновлення інтерфейсу користувача при зміні властивостей об'єкта (ів) (Automatic UI Update). За таким же принципом працює INotifyPropertyChanged в Silverlight і WPF;
- Відстеження залежностей (Dependency tracking);
- Шаблони (Templating).
Кожен з перерахованих принципів так чи інакше (а в деяких випадках, навіть кілька разів) були представлені на суд громадськості в тій чи іншій формі. Існує величезна безліч контролів, фреймворків і надбудов в мережі, але коли з'явився knockout (далі "ko" або "нокаут"), який об'єднав все перераховане в одному, що називається флаконі, розробка на HTML 5 стала доставляти справжнє задоволення.
Завдання для прикладу з використанням Knockout
Готуємо до старту
Створюємо новий проект.

Далі треба б оновити всі пакети, для цього запускаємо команду Update-Package - поновлення в консолі Nuget Manager. У новому шаблоні MVC4 проекту вже існує файл knockout. *. Js. А якщо ви вирішите використовувати MVC3, то вам доведеться додатково встановити nuget-пакет knockout В результаті роботи вийшов великий список оновлень і це незважаючи на те, що студія вийшла не більше місяця назад (для передплатників MSDN):
Додамо ще один пакет MvcTools:
Я відразу видалив файл _Layout.cshtml. а що з'явився після установки цього пакета _LayoutExtended.cshtml поставив як стартовий за замовчуванням. Це робиться в файлі _ViewStart.cshtml:
Зараз проект не запуститься, треба в новому шаблоні поправити "_LogOnPartial" на (новий для MVC4 файл) "_LoginPartial". А тепер досить замінити код внизу сторінки:
на новий (знову ж бо ми використовуємо MVC4, для MVC3 нічого міняти не треба в головному шаблоні):
Трохи пізніше в цей список ми додамо скрипти для knockout. Запустимо проект - Так! Запустився.

Примітка: Зовнішній вигляд зміненого шаблону не дуже який, але зате яка свобода для творчості! Не залишати ж шаблон за замовчуванням. Але насправді, я ще встиг переробити nuget-пакет для MVC4.
А зараз поставимо ще один пакет Knockout.Mapping:
І після цього ще один пакет:
В папці Scripts з'явилися новенькі файли, а ми створю папку Js щоб складувати туди скрипти, які будемо писати самі. А зараз поки відкладемо цю папку в "сторону".
Знову моделі? Ага, бо MVC
Створимо простий клас FeedbackViewModel. який буде заповнювати користувач для відправки форми зворотного зв'язку:
AjaxController
Створюємо новий контролер під назвою AjaxController. він буде успадкований від базового класу Controller (Про ApiController і WebAPI ми поговоримо наступного разу).
Отже, метод, який повертає список тем повідомлення:
Уявлення (View)
А тепер давайте підправимо головну сторінку сайту. Відкриємо Index.cshtml і видаливши все зайве залишимо таку розмітку. Саме з головної форми ми будемо відправляти повідомлення (feedback). Отже, розмітка:
Далі ми її будемо дописувати. Тепер підключимо knockout і все що необхідно для його роботи в головному шаблоні проекту, щоб на конкретних уявлення (view) досить було підключити тільки скрипт з ViewModel'ом цього подання. Вийшло не дуже зрозуміло, і тому прошу вибачення за каламбур. По ходу далі буде зрозуміліше.
Так як ми використовуємо MVC4 можна скористатися Bundles (це щось нове і жахливо корисне з того, що з'явилося в MVC4). В папці App_Start є файл BundleConfig.cs.
Зверніть увагу на рядок номер 2. У головному шаблоні _LayoutExtended.cshtml у нас є рядок:
Це означає, що всі скрипти зареєстровані в цьому пакеті (від англійського Bundle - "пакет") будуть завантажуватися на всі сторінки сайту. Я просто додам в цей пакет ще парочку рядків:
Я за звичкою підключив відразу все, що називається "до купи": і сам нокаут, і розширення маппінга, і валідацію введення для нокауту. Хоча для такого просто проекту цього й не було потрібно (зате ви тепер знаєте про ці розширення). Тепер я можу перевірити, що скрипти нокауту вантажаться:

Тепер почнемо, власне кажучи, саме програмування.
Я вважаю за краще розділяти функціонал по різних файлах, тим більше, що в MVC4 з'явилася така прекрасна річ як мінімізація і пакетування (Bundles). Я створив файл site.core.js. в якому підготував обгортки для роботи з jQuery.Ajax. Ось частина коду файлу:
Також я створив файл site.services.feedback.js. Цей файл містить безпосередньо сам data-сервіс, який і буде використовуючи обгортку site.services.ajax щоб звертатися до методів AjaxController:
Зверніть увагу на рядки 17 і 23, саме в них і викликаються методи контролера.
І, власне кажучи, ще один файл site.vm.feedback.js. який вже є ViewModel'ом для подання Index.cshtml:
Цей ViewModel може поки тільки завантажити список тем для повідомлень, ми його доопрацюємо пізніше. Назвемо цей лістинг - "модель 1". Далі по ходу статті, я буду на нього посилатися.
Для того щоб підключити ці скрипти, на сторінці Index.cshtml напишу такий код в самому низу сторінки, щоб не заважав:
Бібліотеки все довантажуючи (на головному шаблоні), скрипти підключені (на сторінці Index.cshtml) - переходимо до самої розмітки.
Декларативна прив'язка (Declarative binding)
Тепер найцікавіше. Я постараюся пояснити, що ж таке декларативна прив'язка (MVVM) на конкретному прикладі. Для початку додам код, який виведе на форму найменування (див. "Модель 1" рядок 15) і кількість завантажених тим повідомлень (див. "Модель 1" рядок 17):
Ось таким незатейлевим способом з використанням атрибута HTML5 "data- ..." це можна зробити. Тема вистави прив'язується до HTML-розмітки в рядку 5, кількість завантажених тим - в рядку 7. А ось так це виглядає:

Тепер відобразимо список тем в елементі
- . Для це доведеться використовувати шаблон. Благо що починаючи з версії нокауту 2.0 з'явилася підтримка власних (nativeTemplateEngine) шаблонів. Код для відображення списку тем повідомлень з використанням шаблонів knockout:
Доданий елемент списку (див. Рядок 6) використовує шаблон (див. Рядки з 13-15). Ось тепер як це виглядає:

Відмінно! Ось тільки мені потрібен список, що випадає. Легко для цього поміняємо елемент і прив'язку на новий тип :.
Я видалив
- разом з шаблоном і поставив