Layout in ASP.NET Core

Di Steve Smith e Dave Brock

Le pagine e le visualizzazioni condividono spesso elementi visivi e programmatici. Questo articolo illustra come:

  • Usare layout comuni.
  • Condividere direttive.
  • Eseguire codice comune prima del rendering di pagine o visualizzazioni.

Questo documento illustra i layout per i due diversi approcci a ASP.NET Core MVC: Razor Pagine e controller con visualizzazioni. Per questo argomento le differenze siano minime:

  • Razor Le pagine si trovano nella cartella Pages .
  • I controller con visualizzazioni usano una cartella Views per le visualizzazioni.

Che cos'è il layout?

La maggior parte delle applicazioni web presenta un layout comune che fornisce all'utente un'esperienza omogenea, nel passare da una pagina a un'altra. In genere, il layout comprende elementi dell'interfaccia utente comune, ad esempio l'intestazione dell'app, la navigazione o elementi di menu e piè di pagina.

Page Layout example

Anche le strutture HTML comuni, ad esempio gli script e i fogli di stile, vengono spesso usate da molte pagine all'interno di un'app. Tutti questi elementi condivisi possono essere definiti in un file di layout, cui è possibile fare riferimento da qualsiasi visualizzazione utilizzata all'interno dell'app. I layout riducono il codice duplicato nelle visualizzazioni.

Per convenzione, il layout predefinito per un'app ASP.NET Core è denominato _Layout.cshtml. I file di layout per i nuovi progetti ASP.NET Core creati con i modelli sono:

  • Razor Pagine: Pages/Shared/_Layout.cshtml

    Pages folder in Solution Explorer

  • Controller con visualizzazioni: Views/Shared/_Layout.cshtml

    Views folder in Solution Explorer

Il layout definisce un modello di primo livello per le visualizzazioni nell'app. Le app non richiedono un layout. Le app possono definire più di un layout, con visualizzazioni diverse che specificano layout diversi.

Il codice seguente mostra il file di layout per un progetto creato da modello con un controller e visualizzazioni:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - WebApplication1</title>

    <environment include="Development">
        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
        <link rel="stylesheet" href="~/css/site.css" />
    </environment>
    <environment exclude="Development">
        <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
              asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
        <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
    </environment>
</head>
<body>
    <nav 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="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a asp-page="/Index" class="navbar-brand">WebApplication1</a>
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a asp-page="/Index">Home</a></li>
                    <li><a asp-page="/About">About</a></li>
                    <li><a asp-page="/Contact">Contact</a></li>
                </ul>
            </div>
        </div>
    </nav>

    <partial name="_CookieConsentPartial" />

    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; 2018 - WebApplication1</p>
        </footer>
    </div>

    <environment include="Development">
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
        <script src="~/js/site.js" asp-append-version="true"></script>
    </environment>
    <environment exclude="Development">
        <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.3.1.min.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
                asp-fallback-test="window.jQuery"
                crossorigin="anonymous"
                integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT">
        </script>
        <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
                asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
                crossorigin="anonymous"
                integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa">
        </script>
        <script src="~/js/site.min.js" asp-append-version="true"></script>
    </environment>

    @RenderSection("Scripts", required: false)
</body>
</html>

Definizione di un layout

Razor le visualizzazioni hanno una Layout proprietà . Le visualizzazioni singole specificano un layout impostando la seguente proprietà:

@{
    Layout = "_Layout";
}

Il layout specificato può usare un percorso completo (ad esempio, /Pages/Shared/_Layout.cshtml o ) o /Views/Shared/_Layout.cshtmlun nome parziale (ad esempio: _Layout). Quando viene specificato un nome parziale, il Razor motore di visualizzazione cerca il file di layout usando il processo di individuazione standard. La ricerca viene eseguita prima nella cartella in cui è presente il metodo gestore (o controller) e poi nella cartella Shared. Questo processo di individuazione è identico a quello usato per individuare le visualizzazioni parziali.

Per impostazione predefinita, ogni layout deve chiamare RenderBody. Quando viene eseguita la chiamata a RenderBody, viene eseguito il rendering del contenuto della visualizzazione.

Sezioni

Un layout può facoltativamente fare riferimento a una o più sezioni, chiamando RenderSection. Le sezioni forniscono un modo per organizzare la posizione in cui devono essere inseriti determinati elementi di pagina. Ogni chiamata a RenderSection può specificare se tale sezione è obbligatoria o facoltativa:

<script type="text/javascript" src="~/scripts/global.js"></script>

@RenderSection("Scripts", required: false)

Se una sezione richiesta non viene trovata, viene generata un'eccezione. Le singole visualizzazioni specificano il contenuto di cui eseguire il rendering all'interno di una sezione usando la @sectionRazor sintassi . Se una pagina o una visualizzazione definisce una sezione, è necessario eseguirne il rendering (o si verifica un errore).

Definizione di esempio @section nella Razor visualizzazione Pagine:

@section Scripts {
     <script type="text/javascript" src="~/scripts/main.js"></script>
}

Nel codice scripts/main.js precedente viene aggiunto alla scripts sezione di una pagina o di una vista. Altre pagine o visualizzazioni nella stessa app potrebbero non richiedere questo script e non definire una sezione scripts.

Il markup seguente usa l'helper tag parziale per eseguire il rendering _ValidationScriptsPartial.cshtmldi :

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Il markup precedente è stato generato dallo scaffolding Identity.

Le sezioni definite in una pagina o in una visualizzazione sono disponibili solo nella relativa pagina di layout immediato. Non è possibile farvi riferimento da righe parzialmente eseguite, componenti di visualizzazione o altre parti del sistema di visualizzazione.

Esclusione di sezioni

Per impostazione predefinita, la pagina di layout deve eseguire il rendering della parte principale e di tutte le sezioni di una pagina di contenuto. Il Razor motore di visualizzazione applica questa impostazione verificando se è stato eseguito il rendering del corpo e di ogni sezione.

Per indicare al motore di visualizzazione di escludere la parte principale o le sezioni, chiamare i metodi IgnoreBody e IgnoreSection.

Il corpo e ogni sezione di una Razor pagina devono essere sottoposti a rendering o ignorati.

Importazione delle direttive condivise

Le visualizzazioni e le pagine possono usare Razor direttive per importare gli spazi dei nomi e usare l'inserimento delle dipendenze. Le direttive condivise da numerose visualizzazioni possono essere specificate in un file _ViewImports.cshtml comune. Il file _ViewImports supporta le direttive seguenti:

  • @addTagHelper
  • @removeTagHelper
  • @tagHelperPrefix
  • @using
  • @model
  • @inherits
  • @inject
  • @namespace

Il file non supporta altre Razor funzionalità, ad esempio funzioni e definizioni di sezione.

Esempio di file _ViewImports.cshtml:

@using WebApplication1
@using WebApplication1.Models
@using WebApplication1.Models.AccountViewModels
@using WebApplication1.Models.ManageViewModels
@using Microsoft.AspNetCore.Identity
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Il _ViewImports.cshtml file per un'app MVC core ASP.NET viene in genere inserito nella cartella Pages (o Views). Un _ViewImports.cshtml file può essere inserito all'interno di qualsiasi cartella, nel qual caso verrà applicato solo alle pagine o alle visualizzazioni all'interno di tale cartella e delle relative sottocartelle. I file _ViewImports vengono elaborati a partire dal livello radice e quindi per ogni cartella fino alla posizione della pagina o della visualizzazione stessa. Le impostazioni _ViewImports specificate al livello radice possono essere sottoposto a override a livello di cartella.

Si supponga ad esempio che:

  • Il file a livello _ViewImports.cshtml radice include @model MyModel1 e @addTagHelper *, MyTagHelper1.
  • Un file di _ViewImports.cshtml sottocartella include @model MyModel2 e @addTagHelper *, MyTagHelper2.

Le pagine e le visualizzazioni nella sottocartella avranno accesso sia agli helper tag che al modello MyModel2.

Se nella gerarchia dei file vengono trovati più _ViewImports.cshtml file, il comportamento combinato delle direttive è:

  • @addTagHelper, @removeTagHelper: eseguiti nell'ordine
  • @tagHelperPrefix: il file più vicino alla visualizzazione esegue l'override di tutti gli altri
  • @model: il file più vicino alla visualizzazione esegue l'override di tutti gli altri
  • @inherits: il file più vicino alla visualizzazione esegue l'override di tutti gli altri
  • @using: vengono inclusi tutti; i duplicati vengono esclusi
  • @inject: per ogni proprietà, quella più vicina alla visualizzazione esegue l'override di tutte le altre con lo stesso nome di proprietà

Esecuzione di codice prima di ogni visualizzazione

Codice che deve essere eseguito prima che ogni visualizzazione o pagina venga inserita nel _ViewStart.cshtml file. Per convenzione, il _ViewStart.cshtml file si trova nella cartella Pages (o Views). Le istruzioni elencate in _ViewStart.cshtml vengono eseguite prima di ogni visualizzazione completa (non dei layout e delle visualizzazioni parziali). Come ViewImports.cshtml, _ViewStart.cshtml è di tipo gerarchico. Se un _ViewStart.cshtml file viene definito nella cartella di visualizzazione o pagine, verrà eseguito dopo quello definito nella radice della cartella Pages (o Views) (se presente).

Esempio di file _ViewStart.cshtml:

@{
    Layout = "_Layout";
}

Il file precedente specifica che tutte le visualizzazioni useranno il layout _Layout.cshtml.

_ViewStart.cshtmle non vengono in genere inseriti nella cartella /Pages/Shared (o /Views/Shared)._ViewImports.cshtml Le versioni a livello di app di questi file devono essere posizionate direttamente nella cartella /Pages (o /Views).