Riutilizzare l'interfaccia utente tramite pagine master e visualizzazioni parziali
da Microsoft
Questo è il passaggio 7 di un'esercitazione gratuita "NerdDinner" che illustra come creare un'applicazione Web di piccole dimensioni, ma completa usando ASP.NET MVC 1.
Il passaggio 7 esamina i modi in cui è possibile applicare il "principio DRY" nei modelli di visualizzazione per eliminare la duplicazione del codice, usando modelli di visualizzazione parziale e pagine master.
Se si usa ASP.NET MVC 3, è consigliabile seguire le esercitazioni di Introduzione With MVC 3 o MVC Music Store.
NerdDinner Passaggio 7: Pagine parziali e master
Una delle filosofie di progettazione ASP.NET MVC abbraccia il principio "Do Not Repeat Yourself" (comunemente definito "DRY"). Una progettazione DRY consente di eliminare la duplicazione del codice e della logica, che rende le applicazioni più veloci per la compilazione e la manutenzione.
Abbiamo già visto il principio DRY applicato in diversi scenari NerdDinner. Alcuni esempi: la logica di convalida viene implementata all'interno del livello del modello, che consente di applicare in entrambi gli scenari di modifica e creazione nel controller; viene riesezionato il modello di visualizzazione "NotFound" tra i metodi di azione Modifica, Dettagli ed Eliminazione; si usa un modello di denominazione convenzione con i modelli di visualizzazione, che elimina la necessità di specificare in modo esplicito il nome quando si chiama il metodo helper View(); e viene riesezionato la classe DinnerFormViewModel per gli scenari di azione Edit e Create.
Ora esaminiamo i modi in cui è possibile applicare il "principio DRY" all'interno dei modelli di visualizzazione per eliminare anche la duplicazione del codice.
Riprovare a modificare e creare modelli di visualizzazione
Attualmente si usano due modelli di visualizzazione diversi, "Edit.aspx" e "Create.aspx" per visualizzare l'interfaccia utente del modulo Cena. Un rapido confronto visivo tra loro evidenzia il modo in cui sono simili. Di seguito è riportato l'aspetto del modulo di creazione:
E qui è simile al modulo "Modifica":
Non c'è molta differenza? Oltre al titolo e al testo dell'intestazione, i controlli di layout e input del modulo sono identici.
Se si apriranno i modelli di visualizzazione "Edit.aspx" e "Create.aspx" si troverà che contengono codice di controllo del modulo identico. Questa duplicazione significa che dobbiamo apportare modifiche due volte ogni volta che introduciamo o cambiamo una nuova proprietà Dinner , che non è buona.
Uso di modelli di visualizzazione parziale
ASP.NET MVC supporta la possibilità di definire modelli di "visualizzazione parziale" che possono essere usati per incapsulare la logica di rendering della visualizzazione per una parte secondaria di una pagina. "Parziali" offre un modo utile per definire la logica di rendering della visualizzazione una volta e quindi usarla nuovamente in più posizioni in un'applicazione.
Per facilitare la duplicazione del modello di visualizzazione Edit.aspx e Create.aspx, è possibile creare un modello di visualizzazione parziale denominato "DinnerForm.ascx" che incapsula il layout del modulo e gli elementi di input comuni a entrambi. Questa operazione verrà eseguita facendo clic con il pulsante destro del mouse sulla directory /Views/Dinners e scegliendo il comando di menu "Aggiungi-visualizzazione>":
Verrà visualizzata la finestra di dialogo "Aggiungi visualizzazione". Verrà denominata la nuova visualizzazione che si vuole creare "DinnerForm", selezionare la casella di controllo "Crea una visualizzazione parziale" nella finestra di dialogo e indicare che verrà passata una classe DinnerFormViewModel:
Quando si fa clic sul pulsante "Aggiungi", Visual Studio creerà un nuovo modello di visualizzazione "DinnerForm.ascx" all'interno della directory "\Views\Dinners".
È quindi possibile copiare/incollare il codice di controllo del modulo duplicato dal modello di visualizzazione Edit.aspx/ Create.aspx nel nuovo modello di visualizzazione parziale "DinnerForm.ascx":
<%= Html.ValidationSummary("Please correct the errors and try again.") %>
<% using (Html.BeginForm()) { %>
<fieldset>
<p>
<label for="Title">Dinner Title:</label>
<%= Html.TextBox("Title", Model.Dinner.Title) %>
<%=Html.ValidationMessage("Title", "*") %>
</p>
<p>
<label for="EventDate">Event Date:</label>
<%= Html.TextBox("EventDate", Model.Dinner.EventDate) %>
<%= Html.ValidationMessage("EventDate", "*") %>
</p>
<p>
<label for="Description">Description:</label>
<%= Html.TextArea("Description", Model.Dinner.Description) %>
<%= Html.ValidationMessage("Description", "*") %>
</p>
<p>
<label for="Address">Address:</label>
<%= Html.TextBox("Address", Model.Dinner.Address) %>
<%= Html.ValidationMessage("Address", "*") %>
</p>
<p>
<label for="Country">Country:</label>
<%= Html.DropDownList("Country", Model.Countries) %>
<%= Html.ValidationMessage("Country", "*") %>
</p>
<p>
<label for="ContactPhone">Contact Phone #:</label>
<%= Html.TextBox("ContactPhone", Model.Dinner.ContactPhone) %>
<%= Html.ValidationMessage("ContactPhone", "*") %>
</p>
<p>
<input type="submit" value="Save"/>
</p>
</fieldset>
<% } %>
È quindi possibile aggiornare i modelli di visualizzazione modifica e creazione per chiamare il modello parziale DinnerForm ed eliminare la duplicazione del modulo. È possibile eseguire questa operazione chiamando Html.RenderPartial("DinnerForm") nei modelli di visualizzazione:
Create.aspx
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
Host a Dinner
</asp:Content>
<asp:Content ID="Create" ContentPlaceHolderID="MainContent" runat="server">
<h2>Host a Dinner</h2>
<% Html.RenderPartial("DinnerForm"); %>
</asp:Content>
Edit.aspx
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
Edit: <%=Html.Encode(Model.Dinner.Title) %>
</asp:Content>
<asp:Content ID="Edit" ContentPlaceHolderID="MainContent" runat="server">
<h2>Edit Dinner</h2>
<% Html.RenderPartial("DinnerForm"); %>
</asp:Content>
È possibile qualificare in modo esplicito il percorso del modello parziale desiderato quando si chiama Html.RenderPartial (ad esempio: ~Views/Dinners/DinnerForm.ascx"). Nel codice precedente, tuttavia, si sfrutta il modello di denominazione basato sulla convenzione all'interno di ASP.NET MVC e si specifica solo "DinnerForm" come nome del rendering parziale. Quando si esegue questa operazione, ASP.NET MVC verrà visualizzata prima nella directory delle visualizzazioni basate su convenzioni (per DinnersController, ovvero /Views/Dinners). Se non trova il modello parziale, cercarlo nella directory /Views/Shared.
Quando Html.RenderPartial() viene chiamato con solo il nome della visualizzazione parziale, ASP.NET MVC passerà alla visualizzazione parziale gli stessi oggetti del dizionario Model e ViewData usati dal modello di visualizzazione chiamante. In alternativa, sono disponibili versioni di overload di Html.RenderPartial() che consentono di passare un oggetto Model alternativo e/o un dizionario ViewData per la visualizzazione parziale da usare. Questo è utile per gli scenari in cui si vuole passare solo un subset dell'oggetto Model/ViewModel completo.
Argomento laterale: perché <% %> invece di <%= %>? |
---|
Una delle cose sottili che si potrebbe aver notato con il codice precedente è che si usa un blocco % %> anziché un <<blocco %= %> quando si chiama Html.RenderPartial(). <%= %> blocchi in ASP.NET indicano che uno sviluppatore vuole eseguire il rendering di un valore specificato(ad esempio: <%= "Hello" %> eseguirà il rendering di "Hello"). <% %> blocchi invece indicano che lo sviluppatore vuole eseguire il codice e che qualsiasi output di cui è stato eseguito il rendering deve essere eseguito in modo esplicito (ad esempio: <% Response.Write("Hello") %>. Il motivo per cui si usa un <blocco % %> con il codice Html.RenderPartial precedente è perché il metodo Html.RenderPartial() non restituisce una stringa e restituisce invece il contenuto direttamente al flusso di output del modello di visualizzazione chiamante. Questa operazione viene eseguita per motivi di efficienza delle prestazioni e, in questo modo, evita la necessità di creare un oggetto stringa temporaneo (potenzialmente molto grande). Ciò riduce l'utilizzo della memoria e migliora la velocità effettiva complessiva dell'applicazione. Un errore comune quando si usa Html.RenderPartial() è dimenticare di aggiungere un punto e virgola alla fine della chiamata quando si trova all'interno di un <blocco % %> . Ad esempio, questo codice causerà un errore del compilatore: % Html.RenderPartial("DinnerForm") % È invece necessario scrivere: <<% Html.RenderPartial("DinnerForm">);> % Questo è perché <> % % blocchi sono istruzioni di codice indipendenti e quando si usano istruzioni di codice C# devono essere terminate con un punto e virgola. |
Uso di modelli di visualizzazione parziale per chiarire il codice
È stato creato il modello di visualizzazione parziale "DinnerForm" per evitare la duplicazione della logica di rendering della visualizzazione in più posizioni. Questo è il motivo più comune per creare modelli di visualizzazione parziale.
A volte è ancora opportuno creare visualizzazioni parziali anche quando vengono chiamate solo in un'unica posizione. I modelli di visualizzazione molto complicati possono spesso diventare molto più facili da leggere quando la logica di rendering della visualizzazione viene estratta e partizionata in uno o più modelli parziali ben denominati.
Si consideri, ad esempio, il frammento di codice seguente dal file Site.master nel progetto (che verrà esaminato a breve). Il codice è relativamente semplice da leggere, in parte perché la logica per visualizzare un collegamento di accesso/disconnessione nella parte superiore destra della schermata viene incapsulato all'interno del parziale "LogOnUserControl":
<div id="header">
<div id="title">
<h1>My MVC Application</h1>
</div>
<div id="logindisplay">
<% Html.RenderPartial("LogOnUserControl"); %>
</div>
<div id="menucontainer">
<ul id="menu">
<li><%=Html.ActionLink("Home", "Index", "Home")%></li>
<li><%=Html.ActionLink("About", "About", "Home")%></li>
</ul>
</div>
</div>
Ogni volta che ci si trova confusa cercando di comprendere il markup html/codice all'interno di un modello di visualizzazione, valutare se non sarebbe più chiaro se alcuni di esso sono stati estratti e refactorati in viste parziali ben denominate.
Pagine master
Oltre a supportare visualizzazioni parziali, ASP.NET MVC supporta anche la possibilità di creare modelli di "pagina master" che possono essere usati per definire il layout comune e il codice HTML di primo livello di un sito. I controlli segnaposto contenuto possono quindi essere aggiunti alla pagina master per identificare le aree sostituibili che possono essere sostituite o "compilate" dalle visualizzazioni. Ciò offre un modo molto efficace (e DRY) di applicare un layout comune in un'applicazione.
Per impostazione predefinita, i nuovi progetti MVC ASP.NET hanno un modello di pagina master aggiunto automaticamente. Questa pagina master è denominata "Site.master" e vive all'interno della cartella \Views\Shared\:
Il file site.master predefinito è simile al seguente. Definisce l'html esterno del sito, insieme a un menu per lo spostamento nella parte superiore. Contiene due controlli segnaposto del contenuto sostituibili, uno per il titolo e l'altro per il quale il contenuto primario di una pagina deve essere sostituito:
<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>
<asp:ContentPlaceHolder ID="TitleContent" runat="server" />
</title>
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="page">
<div id="header">
<div id="title">
<h1>My MVC Application</h1>
</div>
<div id="logindisplay">
<% Html.RenderPartial("LogOnUserControl"); %>
</div>
<div id="menucontainer">
<ul id="menu">
<li><%=Html.ActionLink("Home", "Index", "Home")%></li>
<li><%=Html.ActionLink("About", "About", "Home")%></li>
</ul>
</div>
</div>
<div id="main">
<asp:ContentPlaceHolder ID="MainContent" runat="server" />
</div>
</div>
</body>
</html>
Tutti i modelli di visualizzazione creati per l'applicazione NerdDinner ("Elenco", "Dettagli", "Modifica", "Crea", "NotFound" e così via) sono stati basati su questo modello Site.master. Questa opzione è indicata tramite l'attributo "MasterPageFile" aggiunto per impostazione predefinita alla direttiva %@ Page %> superiore <quando sono state create le visualizzazioni usando la finestra di dialogo "Aggiungi visualizzazione":
<%@ Page Inherits="System.Web.Mvc.ViewPage<NerdDinner.Controllers.DinnerViewModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
Ciò significa che è possibile modificare il contenuto Site.master e applicare automaticamente le modifiche e usare quando si esegue il rendering di uno dei modelli di visualizzazione.
Aggiorniamo la sezione intestazione di Site.master in modo che l'intestazione dell'applicazione sia "NerdDinner" anziché "My MVC Application". Aggiorniamo anche il menu di spostamento in modo che la prima scheda sia "Trova una cena" (gestita dal metodo di azione Index() di HomeController e aggiungiamo una nuova scheda denominata "Host a Dinner" (gestita dal metodo Create() di DinnersController:
<div id="header">
<div id="title">
<h1>NerdDinner</h1>
</div>
<div id="logindisplay">
<% Html.RenderPartial("LoginStatus"); %>
</div>
<div id="menucontainer">
<ul id="menu">
<li><%=Html.ActionLink("Find Dinner", "Index", "Home")%></li>
<li><%=Html.ActionLink("Host Dinner", "Create", "Dinners")%></li>
<li><%=Html.ActionLink("About", "About", "Home")%></li>
</ul>
</div>
</div>
Quando si salva il file Site.master e si aggiorna il browser verranno visualizzate le modifiche all'intestazione in tutte le visualizzazioni all'interno dell'applicazione. Ad esempio:
E con l'URL /Dinners/Edit/[id] :
passaggio successivo
Le pagine parziali e master offrono opzioni molto flessibili che consentono di organizzare in modo pulito le visualizzazioni. Si scoprirà che consentono di evitare di duplicare il contenuto o il codice di visualizzazione e rendere i modelli di visualizzazione più facili da leggere e gestire.
Ora si rivisita lo scenario di presentazione creato in precedenza e si abilita il supporto di paging scalabile.