ASP.NET MVC: История одного проекта "UI - всё для пользователя" (часть 5)
В этой части займемся пользовательским интерфейсом (UI). Моё, сугубо личное мнение, что пользовательский интерфейс (имеется в виду функциональные возможности, а не картинки и разноцветный текст) является одним из самых важных критериев качественного сайта.
Содержание Что имеем и к чему стремитьсяВ предыдущей части были подготовлены шаблоны, классы, репозитории и контроллеры. Теперь пришло время привести внешний вид представлений (Views) экспонатов музея к адекватному виду (про ленту говорить в этой статье не буду). Я обещал, что к моменту написания следующей (этой) части, перенесу данные и пользователей в одну новую базу, и все последующие изменения в структуре данных будут происходить уже по средствам EF миграций (Migrations). Показывать как я проделал операцию переноса не буду, скажу что после экспорта данных в новую базу я запустил aspnet_regsql.exe (об этой утилите было рассказно в первой части), потом завел одного пользователя и добавил ему роль "Администратор". Еще надо в web.config поправить строку подключения к базе с пользователями.
Обратите внимание на значения параметров Initial Catalog для обоих строк - теперь они одинаковые.
Запустил сайт, оглядел страницы музея (про ленту анекдотов на этот раз говорить не будем), и вот что я увидел. Так отображется главная страница для музея юмора (уже с данными):
А вот так выгдядит страница детальная информации:
Ужасный вид, не правда ли? А вот так выглядит форма добавление нового экспоната:
А так форма редактирования:
Душераздирающее зрелище! Особенно не радует тот факт, что я совершенно забыл разграничить доступ. То есть на страницы, которые должны заходить только администраторы, меня сайт запустил без проверки моей учетной записи и, соответстветнно, моих ролей. Сделаем это сейчас. Для этого надо в контроллере Museum на методами Edit и Create поставить атрибут (AuthorizeAttribute), который потребует от пользователя наличие роли "Administrator":
и для методов редактирования:
Теперь, если попытаться открыть страницу с формой добавления или редактирования какого-нибудь экспоната, сайт перенаправит меня на страницу входа. Именно такое поведение я и планировал.
Полезная оптимизация №1Посмотрите на предыдущие примеры кода. Не трудно заметить, что название роли в атрибуте прописаны строкой. Мне "повезло", у меня пока только одна роль, но что делать, если их больше чем одна? Я могу со всей ответственностью заверить вас, что когда вы снова откроете проект (например, для введения какой-нибудь новой функции) через пару-тройку месяцев (или лет), вы не сможете вспомнить какие названия ролей вы прописали при регистрации пользователей (это, конечно же, при условии, что у вас не один и даже не два проекта). Существует несколько способов облегчить себе жизнь. Я поступлю следующим образом. Создаю файл ресурсов, в котором буду хранить все названия строк, которые буду использовать в своем проекте:
Потом создаю новую строку ресурса:
И после всего этого, я могу смело писать свой собственный атрибут авторизации, который будет использовать данные из ресурсного файла:
При таком подходе, я могу, просто изменив (например, при переносе в другую систему функционал связанный с авторизацией) название роли в файле ресурсов, скопировать сборку по работе с авторизованными пользователями. И раз теперь я имею такой атрибут, применим его в контроллере:
и для методов редактирования:
Вот такое небольшое отступление от темы получилось. Надеюсь? оно было полезным (пишите в комментариях, нужные ли они в статье или можно без них). Теперь дальше пройдемся по MVC framework'у.
Лирическое отступление на тему "главная страница"Чтобы привести главную страницу к нормальному виду, я для начала сделаю очередное отступление. Дело в том, что я взял за привычку не "вытаскивать" модели на представления (Views). Каюсь, что не показал как это делается во второй части "истории одного проекта" (далее ИОП) при отрисовке меню сайта (Hall), хотя мне и надо было всего лишь проверить, что контейнер работает правильно. На данный момент у меня меню сайта, также отображается с использованием ViewModel:
И теперь, чтобы быть последовательным, я постараюсь подробно рассказать, как я буду делать это для сущности Exhibit так, как будто у меня это первая сущность, в этом проекте, которая должна превращаться во ViewModel и обратно. Итак поехали.
Model -> ViewModel -> ViewДля начала создаю в папке Models еще одну папку, называю ее ViewModels. Теперь в этой папке создаю новый класс ExhibitViewModel:
Могу догадаться, что в какой-то момент времени, у вас может возникнуть вопрос: "Зачем это ViewModel нужен, если можно показать на предствалении (View) просто модель и не париться?". На что готов ответить следующее. Бывают моменты в процессе разработки, когда приходится менять структуру данных так сильно (речь идет о серьезных, больших проектах. например, на основании требования заказчика), что применение изменений для представлений, репозиториев и контроллеров под это обновление, если не в корне "убивает" всю проделанную до этого работу, то ужасно сильно затягивает процесс введения обновлений и замедляет разработку. Сие утверждение проверено на собственном опыте. Даже если вы больше никогда не будете изменять проект (структуру), хуже от того, что вместо моделей на представлении появятся их "заменители" (ViewModels) точно не будет. Единственное что вы потеряете - немного времени, зато в будующем оно (затраченное время) к вам вернется с лихвой, если придется расширить или обновить уже существующий функционал (структуру данных). Так что мой вам совет: всегда создавайте ViewModel для Model чтобы отобразить ее на представлении (View).
Итак, ViewModel есть, теперь немного расширений. Я создаю файл расширений в папке Engine, под названием ModelsExtensions (namespace я оставил Calabonga.Mvc.Humor), в который напишу расширение для класса Exhibit (и для Hall тоже).
Контроллер тоже надо "поправить" изменив метод Index, который получает данные для главной страницы. Главное изменение выделено жирным:
А вот теперь пришло время поговорить о представлении (View) главной страницы музея юмора. Я немного поколдовал над внешиним видом: теперь страница выглядит так:
Главное отличие от предыдущего вида в том, что это теперь не <table>, а <div>. Я не случайно выделил на картинке место желтым маркером, тут должны быть метки (Tags), Хотя нет. об этом позже. Немного кода разметки. Так выглядит код главной страницы:
Обратите внимание, на жирную строку. Эта строка вызывает отрисовку шаблона для каждого объекта типа Exhibit. Код шаблона для Exhibit:
Вот только Tags указаны в разметке, но не отображаются правильно, это как раз то самое место, которое я выделил на картинке. Надо исправить данный казус. Я буду отрисовывать на странице метки при помощи специльного представления для отображения, который помещается в папку Shared/DisplayTemplates. Потому для редактирования меток к записям будет использоваться другой шаблон в другой папке Shared/EditorTemplates. Создаю папку DisplayTemplates и новое в ней представление (View) с именем Tag.cshtml.
Содержимое этой View:
Теперь мне осталось просто запустить проект. MVC 3 Framework на столько "умный", что сам поймет, что при отображение коллекции меток (Tags) нужно будет использовать именно этот шаблон для отрисовки каждой метки. Я пока просто показываю названия меток, в дальнейшем они должны стать кликабельны.
Я сейчас заметил, что короткие экспонаты отображаются корректно, а вот длинные, занимают очень много места. Надо бы устранить сию оплошность и сделать "обрезание" лишнего текста, тем более, что всё равно любой из экспонатов можно посмотреть в отдельном виде (метод контроллера Details я вместе с представлением переименовал в Show). Я создал класс-помощник, который будет в дальнейшем наращивать свои амбиции и возможности. Пока в нем единственный метод CutLongText.
Осталось правильно его применить на правильном представлении. Но в каком. Да вы совершенно правы! Нужно еще одно преставление (View). Создаю новое представление для сущности Exhibit в папке Templates, назову его ExhibitBriefTemplate:
И, соответственно, я этот шаблон буду использовать в главном представлении главной странице:
Теперь код теперь в самом шаблоне ExhibitBriefTemplate:
Соответственно, теперь сам внешний вид главной страницы музея принял другой вид (экспонаты с длинным текстом отмечены желтым маркером)
Заключение или что дальше.По мере работы над каждым из представлений (просмотр списка, единичного объекта, редактирования, создания), я буду писать новые ViewModel, расширения, представления и даже придется создавать другого рода всякие полезности.