ASP.NET MVC: И снова про форму обратной связи или куда еще втыкнуть Knockout
Сайтостроение | создано: 14.01.2013 | опубликовано: 14.01.2013 | обновлено: 13.01.2024 | просмотров: 15325 | всего комментариев: 6
Уже не раз на страницах блога был материал о форме обратной связи. Но о форме с использованием Knockout еще не было. Про это и будет мой сказ.
Создание и подготовка проекта
У меня студия Visual Studio 2012 Ultimate с лицензией подписчика (не плохо да?), но вы можете воспользоваться и бесплатной версией (VS 2012 Express). Создаю новый проект ASP.NET MVC4. Сразу же, чтобы не терять время запускаю команду обновления всех nuget-пакетов (выполняю команду в Package Manager Console):
PM> Install-Package
… {много букв касаемо обновления пакетов} …
Теперь поставлю несколько дополнительных пакетов, конечно же nuget-пакетов:
PM> Install-Package knockoutjs 'knockoutjs 2.2.0' already installed. FeedbackWithKo already has a reference to 'knockoutjs 2.2.0'. PM> Install-Package jssite Successfully installed 'JsSite 0.2.2'. Successfully added 'JsSite 0.2.2' to FeedbackWithKo. PM> Install-Package knockout.Validation Attempting to resolve dependency 'knockoutjs (≥ 2.0.0)'. Successfully installed 'Knockout.Validation 1.0.1'. Successfully added 'Knockout.Validation 1.0.1' to FeedbackWithKo. PM> Install-Package MvcTools.Mvc4 Attempting to resolve dependency 'XmlExport (≥ 0.2.1)'. Successfully installed 'XmlExport 0.2.2'. Successfully installed 'MvcTools.Mvc4 0.3.5'. Successfully added 'XmlExport 0.2.2' to FeedbackWithKo. Successfully added 'MvcTools.Mvc4 0.3.5' to FeedbackWithKo. PM>
Раскидаю всё по папкам, для удобства, привычка, для красоты (нужное подчеркнуть). Принцип простой, мухи и котлеты отдельно:

Обратите внимание, что App в папке Scripts появится как результат установки пакета JsSite. Далее следует “прибраться” в шаблоне. Для начала удаляю _Layout.cshtml и вместо него в файле _ViewStart.cshtml ставлю использование _LayoutExtended.cshtml:
@{
Layout = "~/Views/Shared/_LayoutExtended.cshtml";
}
В самом шаблоне _LayoutExtended.cshtml добавлю еще одну закладку:
<ul id="menu">
<<li>@Html.ActionLink("Home", "Index", "Home")li>
<<li>@Html.ActionLink("About", "About", "Home")li>
<<li>@Html.ActionLink("Contact", "Contact", "Home")li>
<<li>@Html.ActionLink("Feedback", "Feedback", "Home")li>
ul>
В строке 5 можете наблюдать добавленный пункт меню. А в контроллере HomeController соответственно, нужен метод Feedback:
public ActionResult Feedback() {
return View();
}
К методу полагается еще и представление Feedback.cshtml. Я его тоже создал, но пока оставил его пустым.
А еще я сразу добавил загрузку Bundle для Knockout внизу странице (сразу после регистрации jquery):
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/ko")
@RenderSection("scripts", required: false)
Создание самого пакета сжатия и минимизации в следующей части.
Разберемся с Bundle
В папке App_Start есть файл BundleConfig.cs, в котором уже есть пакеты (bundles), я добавил еще для knockout и, таким образом, вот полный файл:
public class BundleConfig {
// For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725
public static void RegisterBundles(BundleCollection bundles) {
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/lib/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/ko").Include(
"~/Scripts/lib/knockout-{version}.js",
"~/Scripts/lib/knockout.validation.js"));
bundles.Add(new ScriptBundle("~/bundles/site").Include(
"~/Scripts/app/site.core.js",
"~/Scripts/app/site.services.js",
"~/Scripts/app/site.controls.js",
"~/Scripts/app/site.bindingHandlers.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
"~/Scripts/lib/jquery-ui-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/lib/jquery.unobtrusive*",
"~/Scripts/lib/jquery.validate*"));
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/lib/modernizr-*"));
bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"));
bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(
"~/Content/themes/base/jquery.ui.core.css",
"~/Content/themes/base/jquery.ui.resizable.css",
"~/Content/themes/base/jquery.ui.selectable.css",
"~/Content/themes/base/jquery.ui.accordion.css",
"~/Content/themes/base/jquery.ui.autocomplete.css",
"~/Content/themes/base/jquery.ui.button.css",
"~/Content/themes/base/jquery.ui.dialog.css",
"~/Content/themes/base/jquery.ui.slider.css",
"~/Content/themes/base/jquery.ui.tabs.css",
"~/Content/themes/base/jquery.ui.datepicker.css",
"~/Content/themes/base/jquery.ui.progressbar.css",
"~/Content/themes/base/jquery.ui.theme.css"));
}
}
В строках с 7-15 вы можете наблюдать то, что я добавил для работы knockout и для jssite. Кажется ничего не забыл.
Начнем программировать
Для того чтобы всё получилось сначала создаем FeedbackViewModel класс, для того чтобы на было что получать на стороне сервера:
public class FeedbackViewModel {
[Required]
[StringLength(100)]
[Display(Name = "Тема сообщения")]
public string Subject { get; set; }
[Required]
[StringLength(50)]
[Display(Name = "Как к Вам обращаться")]
public string UserName { get; set; }
[Required]
[RegularExpression(@"^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$", ErrorMessage = "Неверный формат электронной почты")]
[StringLength(50)]
[Display(Name = "Email для обратной связи")]
public string EmailAdrress { get; set; }
[Required]
[StringLength(500)]
[DataType(DataType.MultilineText)]
[Display(Name = "Текст сообщения")]
public string Message { get; set; }
}
Теперь создаем контроллер. Я буду использовать простой контроллер, который является наследником от Controller, но вы в праве использовать и ApiController. Вот содержание файла AjaxController.cs:
public class AjaxController : Controller {
[HttpPost]
public JsonResult SendFeedback(FeedbackViewModel model) {
if (ModelState.IsValid) {
// отправляем сообщение...
return Json(new { result = "Сообщение отправлено" });
}
else {
return Json(new { error = "ошибка в заполнении формы" });
}
}
}
На данном этапе серверная часть завершена. Теперь займемся javascript’ами.
JavaScript не желаете?
В папке Scripts/App есть файл site.services.js, в нем примерный код для обращения к сервисам сайта. Я написал такой код сервиса:
///////////////////////////////////////////////////////////////
// simple DataService sample
// автор: calabonga.net
///////////////////////////////////////////////////////////////
(function (site) {
"use strict";
site.services.utilits = {
sendFeedback: function (feedback, callback) {
if (callback === undefined) {throw new Error(200, "callback is undefined");}
site.fw.ajaxService.postJson("SendFeedback", feedback, callback);
}
};
})(site);
Теперь создаю новый файл site.vm.feedback.js, в котором будет ViewModel (на javascript, естественно), для работы формы обратной связи. Приведу весь код, а потом немного прокомментирую:
$(function () {
site.vm.Feedback = function () {
var item = this;
item.subject = ko.observable()
.extend({ required: true, maxLength: 100 });
item.message = ko.observable()
.extend({ required: true, maxLength: 500 });
item.emailadrress = ko.observable()
.extend({ required: true, maxLength: 50, email: true });
item.username = ko.observable()
.extend({ required: true, maxLength: 50 });
return item;
};
site.vm.feedbackViewModel = function () {
var message = ko.observable("заполните форму"),
feedback = new site.vm.Feedback(),
isbusy = ko.observable(false),
issended = ko.observable(false),
errors = ko.validatedObservable(feedback),
send = function () {
isbusy(true);
var jsonData = ko.toJSON(feedback);
site.services.utilits.sendFeedback(jsonData, callback);
},
callback = function (json) {
if (!json.error) {
alert(json.result);
message(json.result);
issended(true);
} else {
alert(json.error);
message(json.error);
}
isbusy(false);
};
return {
issended:issended,
isbusy:isbusy,
errors: errors,
send: send,
feedback: feedback,
message: message
};
}();
ko.applyBindings(site.vm.feedbackViewModel);
});
Строки 3-14: Модель (если хотете “класс”) Feedback.
Строки 6,8,10,12: Рассширения класса Feedback, наложенные для валидации объекта на форме. Для этого используется Knockout.Validation.js.
Строки 16-42: ViewModel формы обратной связи.
Строка 17: Свойство используется для отображения сообщения с сервера о результате отправки формы.
Строка 18: Экземпляр класса Feedback.
Строка 21: Инициализируем валидатор ошибок, задав объект для валидации.
Строка 22-26: Функция отправки сообщения.
Строка 27-37: Обработка результатов отправки, полученных с сервера.
Строка 49: Привязка модели к форме (это самая магическая магия под названием knockout).
Представление формы (View)
Давайте я просто покажу саму форму. А если будут вопросы, буду рад ответить на них:
@{
ViewBag.Title = "Форма обратной связи";
}
<hgroup>
<h2>Форма обратной связиh2>
<h3 data-bind="text: message">h3>
hgroup>
<div data-bind="ifnot: isbusy, ifnot: issended">
<p>
<span>Тема сообщения:span><br />
<input type="text" data-bind="value: feedback.subject" /><br />
<br />
<span>Ваше имя:span><br />
<input type="text" data-bind="value: feedback.username" /><br />
<br />
<span>Электроящик:span><br />
<input type="text" data-bind="value: feedback.emailadrress" /><br />
<br />
<span>Сообщение:span><br />
<textarea data-bind="value: feedback.message">textarea><br />
<br />
<button data-bind="click:send, enable: errors().isValid()">отправитьbutton>
p>
<p>
<span data-bind="text: JSON.stringify(ko.toJS($data), null, 2)">span>
p>
div>
@section scripts {
@Scripts.Render("~/bundles/site")
<script src="~/Scripts/app/site.vm.feedback.js">script>
}
Это полный текст. Давайте отправим какую-нибудь информацию:

Нажем кнопку “отправить”, и…:

Скриншот как заключение
В как результат всей статьи:

Вот и всё. Demo-проект лежит на github. Пишите комментарии.
Комментарии к статье (6)
Спасибо за полезную статью. Вот только что-то я никак не пойму, куда будет отправлено письмо?
EmailAdrress - это адрес отправителя по имени UserName или получателя?
Где прописыватеся куда будет оправлено письмо?
Есть ли какой-то более простой способ прикрутить к сайту форму обратной связи?
Оксана, EmailAdress - в данном примере используется как адрес отправителя, для того чтобы получатель (в моем примере это администратор сайта) мог написать ответ на этот адрес.
Хорошо бы где-то чтобы вы выкладывали весь рабочий пример. У себя до кучи не могу собрать. Есть ?
Спасибо за статью. У меня остался вопрос где заполняется почтовый адрес администратора сайта ?
Nikita
Вы можете заполнять адрес откуда захотите: из базы, из настроек или намертво прописать в код. Этот вопрос не имеет принципиального значения.