ASP.NET MVC: И снова про форму обратной связи или куда еще втыкнуть Knockout
Сайтостроение | создано: 14.01.2013 | опубликовано: 14.01.2013 | обновлено: 13.01.2024 | просмотров: 15017 | всего комментариев: 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
Вы можете заполнять адрес откуда захотите: из базы, из настроек или намертво прописать в код. Этот вопрос не имеет принципиального значения.