ASP.NET MVC: MVVM на HTML или использование knockout при создании сайта (часть 2 из 2).

ru-RU | создано: 16.09.2012 | опубликовано: 16.09.2012 | обновлено: 01.01.2018 | просмотров за всё время: 11693

В этой части закончим начатое в первой части, а именно пример сайта на с формой обратной связи с использованием Knockout.

Доведем форму до ума

В первой части статьи была подготовлена основа для формы обратной связи, в этой части закончим начатое.  Я добавил некоторое количество полей на форму. Теперь разметка формы выглядит так:

<h3 data-bind="text: view.title"></h3>
<p data-bind="text: sendResult"></p>
<div data-bind="ifnot: isbusy, visible: sended">
    <p>
        <label for="Subject">Выберите тему сообщения</label>
        <select data-bind="options: subjects, 
                           value: vm().Subject, 
                           optionsCaption:'выбрать тему'" id="Subject"></select>
        <small>загружено тем:
             <span data-bind="text: subjects().length"></span>шт.</small>
        <br />
        <span data-bind="visible: vm().Subject">Выбранная тема: <b>
            <span data-bind="text: vm().Subject"></span></b>
        </span>
    </p>
    <p>
        <label for="userName">Ваше имя</label>
        <input type="text" data-bind="value: vm().UserName" id="userName" required />
    </p>
    <p>
        <label for="email">Ваш почтовый адрес для ответа</label>
        <input type="email" data-bind="value: vm().Email" id="email" required />
    </p>
    <p>
        <label for="message">Тескт сообщения</label>
        <textarea id="message" data-bind="value: vm().Message" rows="5" required></textarea>
    </p>
<p>
    <button data-bind="click: sendMessage">Отправить сообщение</button>
</p>
</div>

@section scripts {
    @Scripts.Render("~/bundles/koval")
    <script src="@Scripts.Url("~/js/site.core.js")"></script>
    <script src="@Scripts.Url("~/js/site.service.feedback.js")"></script>
    <script src="@Scripts.Url("~/js/site.vm.feedback.js")"></script>
}

Поясню некоторые строки этого листинга:

  • строка 2:  сначала выводит приглашение заполнить все поля, при удачной попытке отправки отображает благодарность, при неудачной, соответственно, сообщение о неработоспособности сервера или вежливое напоминание, что поля надо заполнять правильно;
  • строка 6: была доработана, добавлена дополнительная опция “выбрать тему”;
  • строка 7-11: выбранная тема сообщения показывается именно тут.

Новый JsonResult

Напишем новый метод контролера Ajax, который как раз и должен будет отправлять сообщение администратору.

/// <summary>
/// Отправленная с формы модель
/// будет приходить сюда
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
public JsonResult SendFeedback(FeedbackViewModel feedback) {
    string message = "Простите ошибка. Не могу отправить сообщение. 
           Возможно форма неверно заполнена или проблемы на сервере.";
    bool sended = false;
    if (ModelState.IsValid) {

        // реальная отправка сообщения
        message = "Спасибо за ваше сообщение. Приходите еще!";
        sended = true;
    }
    return Json(new { Message = message, Sended = sended });
}

Опять вернемся Javascript

А теперь на код изменившегося ViewModel’а для javascript:

///////////////////////////////////////////////////////////////
//  site.feedback
//  Работает через dataService с объектами на форме
//  Feedback
//  автор: calabonga.net
///////////////////////////////////////////////////////////////

(function (site) {

    "use strict";

    site.vm.feedbackViewModel = function () {
        var
            view = {
                title: "Отправка сообщения"
            },
            subjects = ko.observableArray([]),
            sendResult = ko.observable("Заполните все поля формы, пожалуйста"),
            isbusy = ko.observable(false),
            cansended = ko.observable(true),
            feedback = ko.observable({
                "Subject": ko.observable(),
                "UserName": ko.observable(),
                "Email": ko.observable(),
                "Message": ko.observable()
            }),
           
            loadSubjects = function () {
                isbusy(true);
                site.services.feedbackForm.loadSubjects(callbackSubjects);
            },
            callbackSubjects = function (json) {
                isbusy(false);
                ko.mapping.fromJS(json, {}, subjects);
                var total = subjects().length;
            },
            sendMessage = function () {
                sendResult("Ждите. Идет отправка сообщения");
                isbusy(true);
                site.services.feedbackForm.sendFeedback(JSON.stringify(feedback), callbackMessage);
            },
            callbackMessage = function (json) {
                isbusy(false);
                cansended(json.Sended ? false : true)
                sendResult(json.Message);
            };

        loadSubjects();

        return {
            view: view,
            vm: feedback,
            isbusy: isbusy,
            sendResult: sendResult,
            sended: cansended,
            subjects: subjects,
            sendMessage: sendMessage
        }

    }();

})(site);

Как не трудно заметить, добавились новые свойства и новый ViewModel для модели FeedbackViewModel:

///////////////////////////////////////////////////////////////
//  ViewModel для отправки feedback на сервер
//  автор: calabonga.net
///////////////////////////////////////////////////////////////

function FeedbackViewModel(subject, userName, email, message) {
    this.Subject = subject;
    this.UserName = userName;
    this.EmailAdrress = email;
    this.Message = message;
};

Вот так теперь выглядит форма обратной связи:

7

А теперь опишу как это работает. Сейчас валидация объекта не предусмотрена. Как можно догадаться, проверку на правильность ввода пользователя проверяет метод контролера SendFeedback. У меня в планах показать, как можно использовать валидацию отправляемого объекта при помощи Knockout.Validation. И это я сделаю чуть позже, а пока посмотрите как работает текущий вариант формы.

Так выглядит форма если пользователь ввел неправильные данные. Подчеркнутое сообщение приходит как результат действия метода контроллера (то есть это серверная валидация):

8

Вот так форма отображается, если пользователь отправил верные данные:

9

Работает всё прекрасно, вот только пользователь, ни коим образом не знает о том, какую именно ошибку он совершил, а если бы форма была посложнее? Очень трудно было бы определить, в каком именно поле была допущена ошибка. Решить данную проблему поможет Knockout.Validation. Раз у ж мы используем ko, то и валидацию будем использовать на его основе, хотя нельзя забывать что существует еще и jquery.validation (и другие, сторонние разработки). Более того, существует вариант использовать jquery.validation в knockout.

Knockout.Validation в действии

Так как сборки (пакеты) уже все подключены. Мне остается немного подправить Javascript ViewModel’а чтобы заработала валидация. Вот измененные строки, которые немного дополнили объявление feedback:

feedback = ko.validatedObservable({
    "Subject": ko.observable().extend({ required: true }),
    "UserName": ko.observable().extend({ required: true, minLength: 4 }),
    "Email": ko.observable().extend({ required: true, email: true }),
    "Message": ko.observable().extend({ required: true, minLength: 10 })
}),

Обратите внимание, что сам объект feedback стал теперь не просто ko.observable(), а ko.validatedObservable(). И еще надо немного на форме подправить привязку формы отправки:

<p>
    <button data-bind="click : sendMessage, 
                       enable: vm().isValid()">Отправить сообщение</button>
</p>

Теперь кнопка отправки будет влючена, только если объект feedback будет валиден (правильно заполнен). Правда же ничего сложного? Если попробовать ввести некорректные данные, форм заартачится и вывалит кучу сообщений:

1

Дополнительная информация по типам валидации расширяемости свойств можно посмтреть на сайте плагина. Кстати, кажется не очень удачная идея отображать русскому пользователю сообщения на английском языке. Можно скачать локализацию положить ее в папку scripts под названием knockout.validation.ru-RU.js. Более ничего делать не надо, даже добавлять в пакет (Bundle), потому файл будет подключен автоматически из-за того, что виртуальный путь до пакета Knockout.Validation использует “*”.

bundles.Add(new ScriptBundle("~/bundles/koval").Include(
                "~/Scripts/knockout.validation.*"

Обновив страницу, можно будет читать сообщения на русском языке.

2

Ну, и на последок, остается немного разукрасить выводимое сообщение, что они были более заметны. Нужно в CSS задать стиль validationMessage.

.validationMessage
{
    background-color:red;
    color:white;
    padding:5px;
}

Вот окончательный вариант формы обратной связи:

3

Вместо заключения

Вот наверное и всё. Пишите комментарии.

Комментарии к статье (6)

16.09.2012 16:15:55 Вячеслав

Спасибо!

17.09.2012 14:29:07 Alex Yakunin

Отличное intro!

14.12.2012 11:36:35 Ярослав

большое спасибо за ввидение в кнокаут!

04.12.2013 19:11:53 Hans

Спасибо, хорошая статья!

09.10.2015 16:39:27 Александр

Вот вопрос а как привязать к

List<string> subjects = new List<string>() {
          "Заявка на регистрацию блога на calabonga.net",
          "Связь с администратором",
          "Связь с блогером",
          "Вопрос об копирайтах",
          "Благодарственное письмо",
          "Желание поблагодарить материально"
     };

этому выражению почту для каждой например для блогера blog@mail.ru

09.10.2015 20:04:19 Calabonga
Александр, потрудитесь пожалуйста объяснить, что вы имели в виду? Зачем адрес электронной почты привязывать к списку выбора темы сообщения?