ASP.NET MVC Урок 7. Bootstrap, jQuery, Ajax

от автора

Цель урока: Определить правила работы с html, js и css файлами. Bootstrap и дополнительный css. Структура js-файлов. Использование jQuery, основные моменты, изучение селекторов, событий и др. addClass, removeClass, attr, data, динамическое создание dom-объекта, ajax.

Наконец мы приступаем к более детальному изучению клиентской части, которая уже в меньшей степени связана с asp.net mvc, но всё равно важна для веб-разработки.

Twitter Bootstrap и css

Twitter Bootstrap – это css-фреймворк. Т.е. набор инструментов для создания блоков, кнопок, меток, форм и навигации. Наше приложение мы будем основывать на этом фрейморке.

Подробнее тут:
http://twitter.github.com/bootstrap/
Установим bootstrap:

Install-Package Twitter.Bootstrap 

Удалим Jquery.UI:

Uninstall-Package Jquery.UI.Combined 

Добавим в BundleConfig bootstap и уберем оттуда jquery.UI (App_Start/BundleConfig.cs):

public class BundleConfig     {         public static void RegisterBundles(BundleCollection bundles)         {             bundles.Add(new ScriptBundle("~/bundles/jquery").Include(                         "~/Scripts/jquery-1.*"));                           bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(                         "~/Scripts/modernizr-*"));              bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(                         "~/Scripts/bootstrap*"));              bundles.Add(new StyleBundle("~/Content/css")                     .Include("~/Content/site.css")  /* не перепутайте порядок */                     .Include("~/Content/bootstrap*"));         }     } 

Важно помнить про порядок приоритета задания стилей:

  • Наименее низким приоритетом обладают встроенные стили браузера
  • Пользовательский стиль в браузере
  • Наследуемые стили от родительских элементов
  • Следующие css-стили заданные во внешних файлах, тут правило важнее (при прочих равных условиях), если файл содержащий правило находится после файла содержащий предыдущее. Это же правило относится и к взаимному расположению правил внутри документа.
  • Далее по стоимости применения селекторов:
    • Теги имеют наименьший приоритет: h1, div, p
    • Классы и псевдоклассы. Селектор: .item { }, :hover { }
    • Аттрибуты [attr=”value”]. Пример: [type=”checkbox”] {}
    • Идентификатор ID – наивысший приоритет. Типа #main { }

  • Еще более высоким приоритетом является задание стиля прямо в style теге:
    <div style=”width : 203px”></div>  

  • И наиболее высоким приоритетом является стиль с сопроводительным словом !important.

Основная работа в задании стилей идет с помощью тегов, классов (псевдоклассов), и атрибутов. Использовать !important не рекомендуется, равно как и задавать стили в атрибуте style и использовать атрибут ID.
Для задания стилей будем использовать css-файл Site.css. Так как в bootstrap уже заданы стили для базовых форм, удалим этот блок и оставим файл (/Content/Site.css):

/* Styles for validation helpers -----------------------------------------------------------*/ .field-validation-error {     color: #f00; }  .field-validation-valid {     display: none; }  .input-validation-error {     border: 1px solid #f00;     background-color: #fee; }  .validation-summary-errors {     font-weight: bold;     color: #f00; }  .validation-summary-valid {     display: none; } 

Это css стили, которые используются для вывода ошибок в методах Html.ValidationMessage(), Html.ValidationSummary().
Теперь давайте определим правила, по которым мы будем создавать свой стиль:

  • Запрещено использовать селектор через id, типа #Main. Это связано с тем, что хотя браузеры и обрабатывают все элементы содержащий данный атрибут, но в правилах DOM атрибут ID должен быть уникальный во всей странице.
  • Классы для офомления используют тип SmallCaps: some-class-name, все с маленькой буквы, для разделения используется “-”. Собственно, так, как это в самом CSS и используется.
  • Классы, предназначенные только для управления (использование исключительно в js-коде) для разделения используют тип lowerCamelCase: someClassName. Собственно, как и соглашения по написанию кода в js или в jquery.
  • Атрибуты id задаются типом UpperCamelCase: id=”SomeButton”.
  • Не используйте классы общего значения, такие как .list, .item, .button, .text, .user, .image, .container, .wrapper на верхнем уровне.
  • Не усложняйте слова. Не используйте конструкцию типа: .main-container-left-part-wrapper-list.
  • Задайте популярные стили если необходимо, делайте это в общем описании типа: .error {color : red }, .left { float : left }.
  • Используйте каскадное описание от общего к частному:
    .list { }      .list .item     {     } <li>используйте селекторы для задания глубины применения стиля     .list > .item      {     } 

  • и для тегов с множественным классом:
    .item.odd { } 

    для

    <div class="item odd"></div> 
  • используйте следующую структуру документа
    /* Главные определения стилей для сайта      - основной шрифт     - междустрочное расстояние     - размер шрифта     - основные цвета ссылок и текста     - h1, h2, h3 ...     - a      - p */  /* Вспомогательные классы:     - .error     - .field-validation-error и др.     - .show-me { border : 1px solid red; }     - .hidden {display : none; }     - .relative { position : relative; }     */  /* Основные вспомогательные окна и стили     - .popup-window      - .popup-window .close      - .popup-window .wrapper     - .popup-window .header      - .error-message     - .info-message     */  /* Основные стили сайта     - header     - .logo     - .user-login     - nav.main-menu     - .main-content     - footer */  /*Главная страница */  /* Страница входа */  /* Страница ошибки */  /* Остальные страницы… */ 

Структура html-страницы.

Подключим стили и js файлы к основному layout файлу (/Areas/Default/Views/Shared/_Layout.cshtml):

<!DOCTYPE html> <html> <head>     <meta charset="utf-8" />     <meta name="viewport" content="width=device-width" />     <title>@ViewBag.Title</title>     @Styles.Render("~/Content/css")     @RenderSection("styles", required: false)     @Scripts.Render("~/bundles/modernizr") </head> <body> <div class="navbar navbar-fixed-top">         <div class="navbar-inner">             <div class="container">                 <ul class="nav nav-pills pull-right">                     @Html.Action("UserLogin", "Home")                 </ul>             </div>         </div>     </div>      @RenderBody()     @Scripts.Render("~/bundles/jquery")     @Scripts.Render("~/bundles/bootstrap")     @RenderSection("scripts", required: false) </body> </html> 

Что здесь происходит:

  1. Мы получаем request и по запросу мы определяем маршрут /Default/Home/Index.
  2. У данного контроллера/метода есть стандартный View – /Home/Index.cshtml

В начале файла объявляется, что он будет включен в Layout:

@{     ViewBag.Title = "LessonProject";     Layout = "~/Areas/Default/Views/Shared/_Layout.cshtml"; } 

Указанный layout выведет данные с помощью @RenderBody(). Запускаем:

Видно, что body заехал под панель навигации. В нашем файле переопределяем body (/Content/Site.css):

body {     padding-top : 40px !important; } 

Гораздо лучше.

Так как мы использовали классы для формы bootstap ранее, то регистрация у нас выглядит теперь так:

Поправим выбор даты рождения, добавим классы в Html.DropDownList() (/Areas/Default/Views/User/Register.cshtml):

… <div class="controls">                 @Html.DropDownList("BirthdateDay", Model.BirthdateDaySelectList, new { @class = "select-day" })                 @Html.DropDownList("BirthdateMonth", Model.BirthdateMonthSelectList, new { @class = "select-month" })                 @Html.DropDownList("BirthdateYear", Model.BirthdateYearSelectList, new { @class = "select-year" })             </div> 

Так как вероятно, мы еще где-то будем использовать эту конструкцию по выбору даты (хотя не факт), то это более общее, чем частное (которое относится именно к регистрации) (/Content/Site.css):

.select-day {     width : 50px; } .select-month {     width : 90px; } .select-year {     width : 70px; } 

Проверяем:

Уиии!

Структура js файлов

Переходим к описанию js файлов. Мы используем jquery как основной фреймворк по работе с клиентской частью кода. Один наш пользовательский js файл (назовем его /Scripts/common.js) будет вызываться всегда. В него будут добавлены те функции, которые будут присутствовать на любой странице. Остальные js-файлы будут вызываться опционально.

Чтобы не путаться создадим 2 папки «admin» и «default» в /Scripts.

Все файлы будут иметь уникальные имена, которые будут записаны в SmallCase формате, и будут относиться к определенной странице (в основном). Например: user-register.js – файл, который будет включен в страницу User/Register.cshtml:

@section scripts {        @Scripts.Render("/Scripts/default/user-register.js") } 

Эта секция выведется в том месте, где описана в _Layout.cshtml (/Areas/Default/Views/Shared/_Layout.cshtml):

…    @Scripts.Render("~/bundles/bootstrap")    @Scripts.Render("~/bundles/common")    @RenderSection("scripts", required: false) </body> 

В /App_Start/BundleConfig.cs тем временем добавим описание:

 bundles.Add(new ScriptBundle("~/bundles/common").Include(                         "~/Scripts/common.js")); 

Все пользовательские js классы, за исключением плагинов, будут иметь следующую структуру:

function FunctionName() {     _this = this;      this.init = function () {         /* установка обработки нажатий или иных манипуляций */         $("button").click(function () {             var id = $(this).attr("id");             _this.saySomething(id);         });     }      /* другие публичные методы*/     this.saySomething = function (id) {         alert("Пыщь-пыщь! : " + id);     }      /* другие приватные методы */     function saySomething (id) {         alert("Пыщь-пыщь! Но тссс!: " + id);     }   }  var functionName = null; $().ready(function () {     functionName = new FunctionName();     functionName.init(); }); 

Рассмотрим подробнее:

  1. function FunctionName имеет имя в стиле UpperCamelCase по имени файла, в котором находится (Common и UserRegister в файлах common.js и user-register.js соответственно)
  2. _this = this; Сохранение ссылки на данную функцию, чтобы была возможность использовать внутри delegate-функций
  3. this.init() – внешняя (public) функция, где будет происходить инициализация обработки.
  4. var functionName = null – создание глобальной переменной. Возможно использование из других файлов.
  5. $().ready() – вызывается после того, как сформирована DOM-структура. JQuery функция.
  6. functionName = new FunctionName(); — создаем объект класса.
  7. functionName.init(); — инициализируем его.
Минификация ресурсных файлов

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

  • Уменьшение количества запросов к ресурсным файлам
  • Уменьшение ресурсных файлов.

Уменьшение количества-ва запросов к изображениям, особенно маленьким. Это делается двумя способами:
?

  1. Использование спрайта. На большой холст добавляется много элементов оформления сразу. После чего в css прописывают смещение и ширину\высоту части этого холста.
    Например рисунок:

    И его использование в html:

    <div class="label label-new sprite"></div> 

    И css:

    .sprite {     background: url("/Media/images/sprite.png");     overflow: hidden;     text-indent: -9999px; } .box .label     	{           position: absolute;           width: 29px;           right: -29px;           top: 35px;     	}      	.box .label-new     	{           background-position: 0 -15px;           height: 119px; } 

  2. 2. Использовать для очень маленьких изображений (иконок) объявления напрямую в css c помощью кодирования его в base64.
    Для этого картинку переводят в base64, например на http://webcodertools.com/imagetobase64converter
    После чего добавляют в css по типу:
    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAWCAYAAAABxvaqAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIFdpbmRvd3MiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RDhENzk1MDI1QkREMTFFMkE2QUNDMTBGODg2MjVENjAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RDhENzk1MDM1QkREMTFFMkE2QUNDMTBGODg2MjVENjAiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpEOEQ3OTUwMDVCREQxMUUyQTZBQ0MxMEY4ODYyNUQ2MCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpEOEQ3OTUwMTVCREQxMUUyQTZBQ0MxMEY4ODYyNUQ2MCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PlBqi0kAAAAwSURBVHjaYmBgYGBnghFccIIbRPCiEvwggg8LixfOBbN44AaACU4gZvgJIv4DBBgARTIDD2TeBRAAAAAASUVORK5CYII=); 

Конечно, при загрузке контентных иконок или изображений данный способ не применим.

Для css и js применяется минификация файла, т.е. убираются пробелы и разделители и используются более краткие локальные переменные. Файл или изначально подготавливается минифицированным как для jquery библиотеки или минифицируется на сервере.

Для включения минификации необходимо в Web.config файле изменить директиву compilation:

<compilation debug="false" targetFramework="4.5" /> 

Или напрямую включить в /App_Start/BundleConfig.cs:

BundleTable.EnableOptimizations = true; 

Проверим:

До оптимизации 527 КБ

После оптимизации 251 КБ

Т.е. больше, чем в 2 раза. На самом деле эта величина может быть как меньше, так и больше, в зависимости от того, какие ресурсы мы грузим. Если есть кеширование, то страница при дальнейшей работе загружает только незначительную часть новых файлов.

Установка jQuery
Изначально jquery уже установлен, но если фреймворк обновился, а это делается часто, то очевидно, что необходимо обновить его:
Install-Package JQuery

Далее, мы до этого убрали JQueryUI (http://jqueryui.com/), так как собираемся функции datepicker, modal использовать из того, что предлагает bootstrap. Но в JQueryUI есть необходимый нам функционал взаимодействия, т.е. Draggable, Droppable, Resizable, Selectable и Sortable. Установим их выборочно:

  1. Так как для Install-Package JQuery.UI.Interactions необходима версия jquery < 1.6, то установим вручную.
  2. Выберем custom скачивание с сайта jqueryui.com/download/
  3. Выбираем только Сore и Interactions
  4. качиваем
  5. Переносим css-файлы в /Content/css
  6. Переносим js-файлы в /Scripts (jquery-1.9.1-min.js переносить нет необходимости)
  7. Подключаем в BundleConfig
    	bundles.Add(new ScriptBundle("~/bundles/jqueryui") 	.Include("~/Scripts/jquery-ui-1.*")); 	bundles.Add(new StyleBundle("~/Content/css/jqueryui") .Include("~/Content/jquery-ui-1*")); 

  8. При необходимости, объявляем на страницах.
  9. Готово!
Firebug (Firefox) и Developer Tool (Chrome)

Для удобства отладки в Firefox есть расширение Firebug, а в Chrome есть встроенный механизм Developer Tool. Я рассмотрю пример с Developer Tool. Вызывается по нажатию клавиш –Shift-Ctrl-I.

Давайте изучим его:

  • Вкладка Elements.

    Здесь, в левой части, мы можем увидет DOM-дерево элементов. В правой части — стили. Стили, как и элементы, можно редактировать на ходу, и изменения будут сразу же отображаться в браузере в редактируемой странице. Очень удобно, когда надо поправить расположение объектов на несколько пикселей.
  • Вкладка Resources

    В левой части располагаются все ресурсы, которые были загружены, а в правой можно просмотреть на них
  • Вкладка Network

    Показывает запросы и тайминг этих запросов. Обозначает разными цветами document, css, js файлы, изображения. Показывает размеры файлов.
    При клике на запрос можно подробнее рассмотреть HTTP-запрос, например:
  • Вкладка Sources

    Одна из основных вкладок. Тут можно просматривать js-файлы, устанавливать точки останова, и выполнять отладку приложения. Горячие клавиши F9, F10, F11 как в VisualStudio для отладки.
  • Вкладка Timeline

    Показывает происходящие события в браузере. Я ее никогда не использовал.
  • Вкладка Profiles

    Для профилирования, т.е. нахождения нетривиальных ошибок. Также, никогда не использовал.
  • Вкладка Audits

    На этой вкладке можно проверить страницу на оптимизацию. Т.е. браузер рекомендует сделать некоторые действия, которые приведут к уменьшению кода передаваемого от сервера, сжатия, удаление лишних строк css.
  • Вкладка Console

    Одновременно и командная строка и окно вывода протоколирования. Для вывода лога нужно вызвать команду console.log(“message”).

    Осторожно используйте эту команду в IE, так как когда консоль не открыта, то выдает ошибку.

Селекторы и обход

JQuery – это инструмент, который помогает нам разрабатывать клиентский код под разные браузеры. К тому же является простой и логичной библиотекой.

В основе всего лежит селектор. Селектор позволяет выбрать множество элементов, находящихся в DOM (document object model) и произвести над ними действия, такие как: назначить обработчик события, изменить местопложение, изменить атрибуты, удалить выбранные элементы, добавить в выбранные элементы текст или html, создать объект.

Основное правило пишется как:

$([“правило селектора”][, область выбора])

Если область выбора не задана, то ищется по всему документу: $(document). Это корневой узел всего DOM.
Если область выбора задана, то ищется только в границе этого узла.
Правило селектора задается по принципам назначения css свойств:

  • $(“div”) – выбор всех div-элементов
  • $(“.class”) – выбор всех элементов с имеющимся классом class
  • $(“.class .class1”) – выбор всех элементов с имеющимся классом class1 содержащихся в классе class
  • $(“.class.class1”) – выбор всех элементов с имеющимся классом class1 и class
  • $(“#Id1”) – выбор элемента (одного) с id =Id1
  • $(“[type=’password’]”) – выбор элемента с атрибутом type=’password’
  • $(“div”, $(“#MainPopup”)) – выбор всех элементов div содержащихся в элементе с id=MainPopup
  • $(“input[type=’checkbox’]:checked”) – выбор элементов ввода типа checkbox, которые отмечены галочкой.

Для проверки найден или нет элемент, можно использовать свойство length:

if($("#Id1").length == 0)          {             alert("Элемент не найден")         } 

Для продвижения вверх по дереву элементов от выбранного можно использовать функции .closest(), .parent() или .parents():

  • parent()– возвращает непосредственного родителя (и только его)
  • parents(selector) – возвращает множество всех родителей (если не указан селектор вплоть до body и html)
  • closest(selector) – возвращает ближайшей элемент, который соотвествует селектору, причем сам элемент может быть ним же.

События

Для обработки событий мы назначаем события на элементы селектора. Например:

$(".button").click(function () { alert("Button clicked");        }); 

Какие есть события:

  • События браузера
    • .error() – произошла ошибка выполнения скрипта в браузере
    • .resize() – изменился размер контейнера, к которому данное событие было назначено.
    • .scroll() – контент внутри контейнера «проскроллили» (тут надо понимать автоматический скроллинг, т.е. если у элемента стиль назначен overflow:scroll и содержимое проскроллили).

  • Загрузки документа
    • .load() – загружен элемент. Например, при изменении (назначении) src у тега img.
    • .ready() – вызывается при окончании загрузки DOM документа. Мы его используем постоянно при инициализации классов.
    • .uload() – вызывается, когда пользователь хочет закрыть страницу.

  • События формы
    • .blur() – при потере фокуса у поля ввода
    • .change() – при изменении выбора у выпадающего списка или списка множественного выбора
    • .focus() – при приобретении фокуса у поля ввода
    • .select() – при выделении текста или части текста в поле ввода
    • .submit() – при подтверждении формы ввода

  • События клавиатуры
    • .focusin() – при приобретении фокуса (аналог focus)
    • .focusout() – при потере фокуса (аналог blur)
    • .keydown() – при нажатии клавиши в момент пока клавиша нажата
    • .keypress() – при нажатии и отпускании клавиши
    • .keyup() – при отпускании клавиши

  • События мыши
    • .click() – при клике левой кнопки мыши на элементе
    • .dblclick() – при двойном клике мыши
    • .hover() – при наведении мыши
    • .mousedown(), .mouseup(), .mouseenter(), .mouseleave(), .mousemove(), .mouseout(), .mouseover() – все эти события реагируют на соотвествующие действия мыши относительно элементов.

Надо помнить, что события не распространяются на вновь созданные элементы, хотя и подпадающие под выбор селектора, который выполнялся ранее. Но если мы, по второму разу проинициализируем, то на элементах, где уже была назначена обработка события, это событие будет выполняться дважды.

Для задания постоянной глобальной обработки нужно использовать следующую конструкцию:

$(document).on("click", ".button", function () { alert("Button clicked"); }); 
Атрибуты и значения

Для работы с атрибутами элемента в основном используются функции:

  • .val(value) – задать значение атрибута value, для конструкции select\option – выбирает элемент
  • .val() – получить значение атрибута value, или значение, если это поле ввода
  • .attr(“attr”) – получить значение атрибута attr
  • .attr(“attr”, “value”) – задать значение value атрибуту attr
  • .data(“id”) – получить значение атрибута data-id
  • .data(“id”, “20”) – задать значение 20 атрибуту data-id
  • .css(“width”, “120px”) – задать значение стиля width:120px
  • .css(“width”) – получить значение width
  • .addClass()/.removeClass()/.toggleClass() – добавить/удалить/переключить класс в элементах селектора
Основные манипуляции

Для работы с элементами рассмотрим следующие функции:

  • .show()\.hide() – показать\скрыть
  • .html(htmltext) – в элементах innerHtml задать html текст
  • .text(text) – в тексте элементов задать text
  • .empty() – очистить элементы селектора
  • .remove() – удалить элементы селектора
  • .append() – добавить html-текст или элементы селектора после всех своих листьев
  • .prepend() — добавить html-текст или элементы селектора перед всеми своими листьями
  • .after()/.before() – вставить html-текст или элементы после элемента/перед элементом
  • .appendTo()/.prependTo() – добавить выбранный элемент в конец/начало листьев заданного элемента
Ajax

Рассмотрим основную и главную функцию (в 99% случаев я обходился только ею).

$.ajax({      type: "GET",             url: "/ajaxUrl",             data: { id: id },             beforeSend: function () {                 /* что-то сделать перед */             },             success: function (data) {                 /* обработать результат */             },             error: function () {                 /* обработать ошибку */             }         }); 

Есть и другие параметры, но к ним прибегать стоит в некоторых случаях.
Рассмотрим подробнее параметры:

  • type. GET или POST запрос.
  • url. Если его не задать, или будет по умолчанию, то ajax-запрос будет отправлен на текущую страницу
  • data. В формате json или get — подобная строка, типа «id=1&value=2». Можно использовать серилизацию формы:
    data: $("form").serialize() 

    При этом надо помнить, что при передаче множества одинаковых значений чекбоксов нужно устанавливать параметр
    traditional : true

    beforeSend. Событие, генерирующеся перед непосредственно отправкой формы.
    success. Событие, которое обозначает, что всё хорошо и в data содержится результат выполнения
    error. Событие, которое возникает, если ответ от сервера был отличный от 200 OK.

Ajax-login форма.

Куча теории, пора бы и к практике переходить. Создадим вторую форму входа, которая будет способствовать быстрому входу на сайте. При клике на «Вход» мы переходим не на страницу Входа, вместо нее выскакивает попапокошко с предложением ввести логин прямо сейчас. При ошибочном вводе, форма выдает предупреждение. Обычную форму по адресу /Login оставляем, она нам понадобится.
Попап формы могут использоваться часто, так что будем считать это стандартной процедурой – вызвать Popup по адресу такому-то. Так как попап – всегда один, то создадим для него контейнер в _Layout.cshtml (/Areas/Default/Views/Shared/_Layout.cshtml):

  <div id="PopupWrapper"></div> 

Добавим функциональность в common.js (/Scripts/common.js):

this.showPopup = function (url, callback)     {         $.ajax({             type: "GET",             url: url,             success: function (data)             {                 $(".modal-backdrop").remove();                 var popupWrapper = $("#PopupWrapper");                 popupWrapper.empty();                 popupWrapper.html(data);                 var popup = $(".modal", popupWrapper);                 $(".modal", popupWrapper).modal();                 callback(popup);             }         });     } 

, где .modal() – это функция из bootstrap.js.

Так как Вход у нас на каждой странице, то следущую функциональность добавляем тоже в common.js:

  this.init = function () {         $("#LoginPopup").click(function () {             _this.showPopup("/Login/Ajax", function (modal)             {             });         });     } 

Добавим в контроллере обработчик (/Areas/Default/Controller/LoginController.cs):

[HttpGet]         public ActionResult Ajax()         {             return View(new LoginView());         }          [HttpPost]         public ActionResult Ajax(LoginView loginView)         {             if (ModelState.IsValid)             {                 var user = Auth.Login(loginView.Email, loginView.Password, loginView.IsPersistent);                 if (user != null)                 {                     return RedirectToAction("Index", "Home");                 }                 ModelState["Password"].Errors.Add("Пароли не совпадают");             }             return View(loginView);         } 

Он полностью аналогичен Index, только будет вызываться другой View – «Ajax», создадим его (/Areas/Default/Views/Login/Ajax.cshtml):

@model LessonProject.Models.ViewModels.LoginView  <div class="modal hide fade" tabindex="-1" role="dialog" aria-hidden="true">     <div class="modal-header">         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>         <h3 id="myModalLabel">Login</h3>     </div>     <div class="modal-body">         @using (Html.BeginForm("Ajax", "Login", FormMethod.Post, new { @class = "form-horizontal", id = "LoginForm" }))         {             <fieldset>                 <legend>Вход</legend>                 <div class="control-group">                     <label class="control-label" for="Email">                         Email</label>                     <div class="controls">                         @Html.TextBox("Email", Model.Email, new { @class = "input-xlarge" })                         <p class="help-block">Введите Email</p>                         @Html.ValidationMessage("Email")                     </div>                  </div>                 <div class="control-group">                     <label class="control-label" for="Password">                         Пароль</label>                     <div class="controls">                         @Html.Password("Password", Model.Password, new { @class = "input-xlarge" })                         @Html.ValidationMessage("Password")                     </div>                 </div>             </fieldset>         }     </div>     <div class="modal-footer">         <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>         <button class="btn btn-primary" id="LoginButton">Login</button>     </div> </div> 

Обратите внимание на id формы LoginForm и id кнопки LoginButton

Изменим вызов в UserLogin.cshtml (/Areas/Default/Views/Home/UserLogin.cshtml):

  <li><span class="btn btn-link" id="LoginPopup">Вход</span></li> 

В common.js добавим обработку LoginButton, при вызове установим обработчик события на $(“#LoginButton”).click(…) (/Scripts/common.js):

this.init = function () {         $("#LoginPopup").click(function () {             _this.showPopup("/Login/Ajax", initLoginPopup);         });     } …    function initLoginPopup(modal) {         $("#LoginButton").click(function () {             $.ajax({                 type: "POST",                 url: "/Login/Ajax",                 data : $("#LoginForm").serialize(),                 success: function (data) {                     showModalData(data);                     initLoginPopup(modal);                 }             });         });     }      function showModalData(data, callback) {         $(".modal-backdrop").remove();         var popupWrapper = $("#PopupWrapper");         popupWrapper.empty();         popupWrapper.html(data);         var popup = $(".modal", popupWrapper);         $(".modal", popupWrapper).modal();         if (callback != undefined) {             callback(popup);         }     } 

Обратите внимание на рекурсивный вызов initLoginPopup. И тут заключается дилемма. Так как при удачном входе нам не надо чтобы в PopupWrapper грузилась новая страница (или страница с ошибкой), а только чтобы страница обновилась.

Для этого сделаем хитрость. В /Areas/Default/Views/Shared/ добавим _Ok.cshtml, суть которого — перезагружать страницу:

<script>     window.location.reload(); </script> 

При удачном входе мы загружаем этот View. При добавлении в дерево DOM в строке

popupWrapper.html(data); 

скрипт запустится и перезагрузит страницу, не дожидаясь остальных вызовов. Изменим контроллер (/Areas/Default/Controllers/LoginController.cs):

var user = Auth.Login(loginView.Email, loginView.Password, loginView.IsPersistent); if (user != null) {     return View("_Ok"); } 

Проверяем, работает!

Итог

Мы рассмотрели основные принципы верстки и клиентской части, но это лишь малая толика того, что вообще можно знать о верстке, стилях и программировании в клиентской части.
Мы научились пользоваться отладчиком в Chrome и создавать ajax запрос. Подробнее рассмотрите этот вопрос в дальнейшем.
Полезные ссылки:
http://jquery.com
http://habrahabr.ru/post/161895/
http://habrahabr.ru/post/154687/
http://twitter.github.com/bootstrap/
http://habrahabr.ru/post/160177/

Все исходники находятся по адресу https://bitbucket.org/chernikov/lessons

ссылка на оригинал статьи http://habrahabr.ru/post/176053/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *