Interfejs użytkownika w aplikacjach web
- Język HTML pozwala opisać strukturę informacji zawartych wewnątrz strony internetowej. CSS wprowadza dodatkową kontrolę nad wyglądem i prezentacją strony internetowej. Kombinacja HTML i CSS pozwala różnym stronom wyglądać inaczej od pozostałych pomimo użycia tych samych konstrukcji.
- Podstawową funkcją ASP.NET MVC jest dostarczanie informacji do użytkownika serwisu. HTML i CSS pozwalają na formatowanie tych informacji w atrakcyjny wizualnie sposób.
- Dynamiczna zawartość strony jest głównym powodem do korzystania z ASP.NET MVC.
- Używając silnika wyświetlania Razor, plik Views\Shared_Layout.cshtml zawiera główny szablon aplikacji a w tym odnośnik do plików ze stylami CSS, podstawowe elementy UI jak na menu, nagłówki i stopkę.
- Silnik wyświetlania ASPX używa pliku Views\Shared\Site.Master.
- Plik szablonu bazowego dziedziczy z System.Web.Mvc.ViewMasterPage niezależnie od silnika wyświetlania.
<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
- Helpery są konstrukcjami kodu ASP.NET MVC tworzącymi kod HTML.
Helper wyświetlający style css w silniku Razor:
@Styles.Render("~/Content/css")
Przykład szablonu w pliku Views\Shared_Layout.cshtml:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>@ViewBag.Title - My ASP.NET MVC Application</title> <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" /> <meta name="viewport" content="width=device-width" /> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") </head> <body> <header> <div class="content-wrapper"> <div class="float-left"> <p class="site-title"> @Html.ActionLink("your logo here", "Index", "Home") </p> </div> <div class="float-right"> <section id="login"> @Html.Partial("_LoginPartial") </section> <nav> <ul id="menu"> <li>@Html.ActionLink("Home", "Index", "Home")</li> <li>@Html.ActionLink("About", "About", "Home")</li> <li>@Html.ActionLink("Contact", "Contact", "Home")</li> </ul> </nav> </div> </div> </header> </body>
Nowe tagi HTML 5
-
- sekcja strony zawierająca niezależną treść.
-
- treść powiązana do otaczającej treści.
-
- podpis ilustracji.
-
- ilustracja.
-
- stopka dokumentu lub sekcji.
-
- nagłówek dokumentu lub sekcji.
-
- sekcja z odnośnikami.
-
- grupa treści które są ze sobą powiązane.
Projektowanie zachowania UI
- Walidacja po stronie klienta jest istotną właściwością JavaScript i ASP.NET MVC dzięki której eliminowane są przesyłania danych pomiędzy klientem a serwerem poprzez sprawdzenie po stronie klienta czy dane wprowadzone do formularza są poprawne. Zasady walidacji ASP.NET MVC są zbudowane w oparciu o adnotacje. Przykład klasy z adnotacjami służącymi do walidacji:
namespace ArticleApp.Models { public class Article { public int ID { get; set; } [Required] [StringLength(50,MinimumLength=5)] public string Title { get; set; } [RegularExpression[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}")] AuthorEmail { get; set;} [DataType(DataType.Date)] [Range(300, 3000)] public int NumberOfAuthors { get; set; } [Required] public DateTime CreateDate { get; set; } [Required] public string Description { get; set; } [Range(1, 250)] [DataType(DataType.Currency)] [Required] public decimal Price { get; set; } } public class ArticleDBContext : DbContext { public DbSet<Article> Articles { get; set; } } }
Przykład widoku dodawania nowego artykułu:
@model MvcApplication1.Models.Article @{ ViewBag.Title = "Create"; } <h2>Create</h2> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>Articles</legend> <div class="editor-label"> @Html.LabelFor(model => model.Title) </div> <div class="editor-field"> @Html.EditorFor(model => model.Title) @Html.ValidationMessageFor(model => model.Title) </div> <div class="editor-label"> @Html.LabelFor(model => model.CreateDate) </div> <div class="editor-field"> @Html.EditorFor(model => model.CreateDate) @Html.ValidationMessageFor(model => model.CreateDate) </div> <div class="editor-label"> @Html.LabelFor(model => model.Description) </div> <div class="editor-field"> @Html.EditorFor(model => model.Description) @Html.ValidationMessageFor(model => model.Description) </div> <div class="editor-label"> @Html.LabelFor(model => model.Price) </div> <div class="editor-field"> @Html.EditorFor(model => model.Price) @Html.ValidationMessageFor(model => model.Price) </div> <p> <input type="submit" value="Create" /> </p> </fieldset> } <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }
Sprawdzenie poprawności modelu po stronie akcji kontrolera:
[HttpPost] public ActionResult Create(Article article) { if (ModelState.IsValid) { db.Articles.Add(article); db.SaveChanges(); return RedirectToAction("Index"); } return View(article); }
- Aby UI mogło wykonać akcję walidacji po stronie serwera należy dodać atrybut System.Web.Mvc.RemoteAttribute do walidacji w modelu. Atrybut Remote akceptuje nazwę kontrolera i akcji które mają być wywołane. Walidacja po stronie serwera wymaga też dodatkowej konfiguracji.
Wpis wymagany w konfiguracji aby można było wykonać walidację po stronie serwera:
<appSettings> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" /> </appSettings>
Przykład modelu z walidacją po stronie serwera:
[Required] [StringLength(6, MinimumLength = 3)] [Remote("IsUserAvailable", "Validation")] [RegularExpression(@"(\S)+", ErrorMessage = "White space is not allowed.")] [Editable(true)] public string UserName { get; set; }
Przykład walidacji po stronie serwera:
public JsonResult IsUserAvailable(string username) { if (!WebSecurity.UserExists(username)) { return Json(true, JsonRequestBehavior.AllowGet); } string suggestedUID = String.Format(CultureInfo.InvariantCulture, "{0} is not available.", username); for (int i = 1; i < 100; i++) { string altCandidate = username + i.ToString(); if (!WebSecurity.UserExists(altCandidate)) { suggestedUID = String.Format(CultureInfo.InvariantCulture, "{0} is not available. Try {1}.", username, altCandidate); break; } } return Json(suggestedUID, JsonRequestBehavior.AllowGet); }
- Biblioteki JavaScript mogą być pomocne przy projektowaniu UI. Przykładowo dzięki jQuery można manipulować drzewem DOM, tworzyć efekty i animacje dzięki czemu strona staje się bardziej interaktywna.
- JavaScript jest interpretowanym językiem skryptowym bazującym na prototypowniu.
Przykład kodu JavaScript używającego prototypów:
var Contact = function(pageTitle) { this.pageTitle = pageTitle; this.bindEvents(); // binding events as soon as the object is instantiated this.additionalEvents(); // additional events such as DOM manipulation etc }; var Contact.prototype.bindEvents = function() { $('ul.menu').on('click', 'li.email, $.proxy(this.toggleEmail, this)); }; var Contact.prototype.toggleEmail = function(e) { //Toggle the email feature on the page };
- AJAX służy do asynchronicznej aktualizacji części strony bez całkowitego jej przeładowania. ASP.NET MVC posiada przestrzeń nazw System.Web.MVC.Ajax która zawiera zestaw rozszerzeń wspierających użycie AJAX.
Przykład formularza używającego AJAX:
@model MvcApplication1.Models.Article @{ ViewBag.Title = "Create"; } <link rel="stylesheet" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" /> <script src="http://code.jquery.com/jquery-1.8.3.js"></script> <script src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script> <link rel="stylesheet" href="/resources/demos/style.css" /> <script> $(function () { $(".ReleaseDate").datepicker(); }); </script> <h2>Create</h2> @using (Ajax.BeginForm("PerformAction", new AjaxOptions { OnSuccess = "OnSuccess", OnFailure = "OnFailure" })) { <fieldset> <legend>Article</legend> <div class="editor-label"> @Html.LabelFor(model => model.Title) </div> <div class="editor-field"> @Html.EditorFor(model => model.Title) @Html.ValidationMessageFor(model => model.Title) </div> <div class="editor-label"> @Html.LabelFor(model => model.CreateDate) </div> <div class="editor-field"> @Html.EditorFor(model => model. CreateDate) @Html.ValidationMessageFor(model => model. CreateDate) </div> <div class="editor-label"> @Html.LabelFor(model => model.Description) </div> <div class="editor-field"> @Html.EditorFor(model => model. Description) @Html.ValidationMessageFor(model => model. Description) </div> <div class="editor-label"> @Html.LabelFor(model => model.Price) </div> <div class="editor-field"> @Html.EditorFor(model => model.Price) @Html.ValidationMessageFor(model => model.Price) </div> <p> <input type="submit" value="Create" /> </p> </fieldset> } <p id="errorMessage" /> <script type="text/javascript"> function OnSuccess(response) { //do something } function OnFailure(response) { //show failure document.getElementById('errorMessage').innerHTML = 'THERE WAS AN ERROR'; } </script> <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }
Komponowanie szablonu UI
Partial view
- Widok częściowy jest sposobem na użycie tej samej funkcjonalności na wielu stronach. Pozwala na zawieranie pod-widoku w wielu miejscach aplikacji. Zwykle przechowuje się je w folderze Views/Shared
- Widok częściowy można utworzyć poprzez menu kontekstowe na folderze z widokami i wybranie opcji Add View oraz zaznaczenie Create as a partial view.
- Użycie modelu w widoku częściowym wymaga dodania jego deklaracji:
@model ApplicationName.Models.ModelName
- Wstawienie widoku do aplikacji odbywa się za pomocą składni @Html.Partial np:
<section id="login"> @Html.Partial("_LoginPartial") </section>
Razor
- Silnik Razor pozwala na tworzenie szablonów które mogą być używane wielokrotnie. Szablony są przypisywane według typu obiektu, mogą służyć do wyświetlania (DisplayTemplates) lub edycji (EditTemplates). Szablony są przechowywane w ~Views/Shared/EditorTemplates i wywoływane przez @Html.EditorFor i @Html.DisplayFor, np:
@Html..EditorFor(model=>model.Article)
- Razor tag @RenderBody() wstawia różne widoki do aplikacji z użyciem tagu
.
- Widoki i widoki częściowe powinny być używane wielokrotnie wszędzie gdzie jest to możliwe. Jeżeli widoki lub widoki częściowe używają tego samego modelu i kontrolera to można sprawdzić poprawność modelu za pomocą adnotacji i helperów HTML. W innym przypadku trzeba obsłużyć sprawdzanie poprawności samemu używając np AJAX do sprawdzenia poprawności po stronie klienta lub modyfikując kontroler i modele do tego zadania.
- Szablony master lub layout mogą być przełączane w locie z wykorzystaniem kodu. ponieważ szablony zawierają zwykle informacje o bibliotekach JavaScript i stylach CSS to ich przełączenie może zmienić znacząco UI.
- Biblioteka Modernizr.js ułatwia pisanie warunkowego kodu JavaScript i CSS w celu ustalenia, czy przeglądarka obsługuje funkcję, zwłaszcza HTML5.
Przykład kompletnego szablonu tworzonego przez Visual Studio:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>@ViewBag.Title - My ASP.NET Application</title> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") </head> <body> <div class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> @Html.ActionLink("Application name", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" }) </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li>@Html.ActionLink("Home", "Index", "Home")</li> <li>@Html.ActionLink("About", "About", "Home")</li> <li>@Html.ActionLink("Contact", "Contact", "Home")</li> </ul> @Html.Partial("_LoginPartial") </div> </div> </div> <div class="container body-content"> @RenderBody() <hr /> <footer> <p>© @DateTime.Now.Year - My ASP.NET Application</p> </footer> </div> @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/bootstrap") @RenderSection("scripts", required: false) </body> </html>
Wykrywanie cech przeglądarki
- Ponieważ nie wszystkie przeglądarki obsługują standardy W3C w taki sam sposób to należy być ostrożnym wybierając sposób wyświetlania informacji. Biblioteki takie jak jQuery lub Modernizer pomagają w dostosowaniu wyglądu we wszystkich przeglądarkach.
- Powszechną metodą wykrywania przeglądarki jest użycie Java Script do sprawdzenia nagłówka userAgent.
Przykład wykrywania czy przeglądarka użytkownika to Microsoft Internet Explorer:
<script type="text/javascript"> if ( navigator.userAgent.indexOf("MSIE")>0 ) { <!--[if lte IE 7]> <style TYPE="text/css"> @import url(ie7.css); </style> <![endif]--> } </script>
Przykład sprawdzenia czy metoda window.addEventListener jest wspierana przez przeglądarkę:
<script type="text/javascript"> if(window.addEventListener) { // Browser supports "addEventListener" window.addEventListener("load", myFunction, false); } else if(window.attachEvent) { // Browser supports "attachEvent" window.attachEvent("onload", myFunction); } </script>
- Ponieważ nie wszystkie przeglądarki w pełni wspierają HTML5 to zalecaną metodą obsługi niektórych cech jest użycie alternatyw dla tych właściwości kiedy przeglądarka nie potrafi obsłużyć zasobu. Przykładowo tag pozwala na użycie alternatywnych zasobów do wyświetlenia i jeżeli wszystkie zawiodą to jeszcze odnośnika z którego można pobrać materiał wideo, np:
<video> <source src="video.mp4" type='video/mp4' /> <source src="video.webm" type='video/webm' /> <object type="application/x-silverlight-2"> <param name="source" value="http://url/player.xap"> <param name="initParams" value="m=http://url/video.mp4"> </object> Download the video <a href="video.mp4">here</a>. </video>
- Aby obsłużyć wiele przeglądarek, w tym także przeglądarki urządzeń mobilnych można użyć różnych widoków dla specyficznych urządzeń lub użyć CSS3 media queries i tagu .
- Informacje o dostępnych rodzajach widoków są dostępne w System.Web.Mvc.VirtualPathProviderViewEngine.DisplayModeProvider. Domyślnie istnieją tam dwa wpisy mobile i default. Widok mobilny może być stworzony w tym samym folderze lecz musi posiadać słowo mobile w nazwie np Index.Mobile.cshtml.
- Dostosowanie widoku dla konkretnej platformy i przeglądarki jest możliwe dzięki DisplayModeProvider. Przykład dodania dostosowania widoku dla Windows Phone:
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iemobile") { ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf ("iemobile", StringComparison.OrdinalIgnoreCase) >= 0) });
Kiedy serwer otrzyma żądanie iemobile to będzie próbował znaleźć widok Index.iemobile.cshtml.
- Aby aplikacja była responsywna i dostosowywała się do rozmiarów przeglądarki można użyć stylu CSS i dodanie tagu . Dodatkowo można użyć @media query które sprawdzają pewne funkcje multimedialne jak szerokość, wysokość i kolory np:
/* header */ header .content-wrapper { padding-top: 20px; } /* logo */ .site-title { color: #c8c8c8; font-family: Rockwell, Consolas, "Courier New", Courier, monospace; font-size: 2.3em; margin: 0; } @media only screen and (max-width: 850px) { /* header mobile */ header .float-left, header .float-right { float: none; } /* logo mobile */ header .site-title { margin: 10px; text-align: center; }
- Aby właściwości CSS3 działały dobrze we wszystkich przeglądarkach można użyć rozszerzeń specyficznych dla dostawcy (vendor prefix) np -ms-, -mso- dla Microsoft, -moz- dla Mozilla, -webkit- dla Google i Apple i -o-, -xv- dla Opera. Przykład CSS z rozszerzeniami specyficznymi dla dostawcy:
<style> .corners { width: 350px; margin: 0px; background-color: #222; color: #fff; padding: 8px; /* regular style */ border-radius: 15px; /* -moz extension */ -moz-border-radius: 18px; } </style>
Planowanie responsywnego układu UI
- ASP.NET MVC obsługuje wiele podejść dla użytkowników mobilnych. Można utworzyć nadpisane widoki, które są uniwersalne dla każdego urządzenia mobilnego lub specyficzne dla urządzenia. System.Web.Mvc.VirtualPathProviderViewEngine.DisplayModeProvider ocenia przychodzące żądania i na podstawie wartości userAgent przekierowuje do skonfigurowanego DisplayModeProviders.
- Innym sposobem jest użycie tagu viewport i @media queries.
- Biblioteka z repozytorium jQuery Mobile MVC umożliwia wykorzystanie znaczników, aby zapewnić dodatkowe funkcje obsługiwane przez przeglądarkę klienta. Jeśli przeglądarka nie obsługuje funkcjonalności, biblioteka jQuery będzie obniżać funkcjonalności.
- Można zmodyfikować plik global.asax w celu dostosowania różnych przeglądarek mobilnych, np:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using System.Web.WebPages; namespace MvcApplication { public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("windows") { ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf ("Windows", StringComparison.OrdinalIgnoreCase) >= 0) }); AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); AuthConfig.RegisterAuth(); } } }