Layout em ASP.NET Core

Por Steve Smith e Dave Brock

Páginas e exibições frequentemente compartilham elementos visuais e programáticos. Este artigo demonstra como:

  • Utilize layouts comuns.
  • Compartilhar diretivas.
  • Execute o código comum antes de renderizar páginas ou exibições.

Este documento discute layouts para as duas abordagens diferentes para ASP.NET Core MVC: Razor Páginas e controladores com visualizações. Para este artigo, as diferenças são mínimas:

  • Razor As páginas estão na pasta Páginas .
  • Controladores com views utilizam uma pasta Views para exibições.

O que é um Layout

A maioria dos aplicativos Web tem um layout comum que fornece ao usuário uma experiência consistente à medida que navega de página em página. O layout normalmente inclui elementos comuns da interface do usuário, como o cabeçalho do aplicativo, elementos de navegação ou menu e rodapé.

Exemplo de Layout de Página

Estruturas HTML comuns, como scripts e folhas de estilo, também são frequentemente usadas por muitas páginas em um aplicativo. Todos esses elementos compartilhados podem ser definidos em um arquivo de layout , que qualquer exibição usada no aplicativo pode referenciar. Os layouts reduzem o código duplicado em exibições.

Por convenção, o layout padrão de um aplicativo ASP.NET Core é nomeado _Layout.cshtml. Os arquivos de layout para novos projetos do ASP.NET Core criados com os modelos são:

  • Razor Páginas: Pages/Shared/_Layout.cshtml

    Pasta Páginas no Gerenciador de Soluções

  • Controlador com exibições: Views/Shared/_Layout.cshtml

    Pasta das exibições no Gerenciador de Soluções

O layout define um modelo de nível superior para exibições no aplicativo. Os aplicativos não exigem um layout. Os aplicativos podem definir mais de um layout, com exibições diferentes especificando layouts diferentes.

O código a seguir mostra o arquivo de layout para um modelo de projeto criado com um controlador e exibições:

<!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>

Especificando um layout

Razor as visualizações possuem uma propriedade Layout. Exibições individuais especificam um layout definindo esta propriedade:

@{
    Layout = "_Layout";
}

O layout especificado pode usar um caminho completo (por exemplo, /Pages/Shared/_Layout.cshtml ou /Views/Shared/_Layout.cshtml) ou um nome parcial (exemplo: _Layout). Quando um nome parcial é fornecido, o mecanismo de exibição Razor pesquisa o arquivo de layout usando seu processo de descoberta padrão. A pasta em que o método de manipulador (ou controlador) existe é pesquisada primeiro, seguida pela pasta Compartilhada . Esse processo de descoberta é idêntico ao processo usado para descobrir exibições parciais.

Por padrão, cada layout deve chamar RenderBody. Sempre que a chamada a RenderBody for feita, o conteúdo da exibição será renderizado.

Seções

Opcionalmente, um layout pode referenciar uma ou mais seções chamando RenderSection. As seções fornecem uma maneira de organizar onde determinados elementos de página devem ser colocados. Cada chamada para RenderSection pode especificar se essa seção é necessária ou opcional.

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

@RenderSection("Scripts", required: false)

Se uma seção obrigatória não for encontrada, uma exceção será gerada. Exibições individuais especificam o conteúdo a ser renderizado em uma seção usando a @sectionRazor sintaxe. Se uma página ou exibição definir uma seção, ela deverá ser renderizada (ou ocorrerá um erro).

Uma definição de exemplo @section no modo de exibição Páginas Razor:

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

No código anterior, scripts/main.js é adicionado à seção scripts em uma página ou exibição. Outras páginas ou exibições no mesmo aplicativo podem não exigir esse script e não definiriam uma seção de scripts.

A marcação a seguir utiliza o Auxiliar de Tag Parcial para renderizar _ValidationScriptsPartial.cshtml:

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

O Andaime Identity gerou a marcação anterior.

As seções definidas em uma página ou exibição estão disponíveis apenas em sua página de layout imediato. Não é possível fazer referência a elas a partir de arquivos parciais, componentes de visualização ou outras partes do sistema de visualização.

Ignorando seções

Por padrão, a página de layout deve renderizar o corpo e todas as seções em uma página de conteúdo. O mecanismo de exibição do Razor impõe isso acompanhando se o corpo e cada seção foram renderizados.

Para instruir o mecanismo de exibição a ignorar o corpo ou as seções, chame os métodos IgnoreBody e IgnoreSection.

O corpo e cada seção em uma página do Razor precisam ser renderizados ou ignorados.

Importando diretivas compartilhadas

Exibições e páginas podem usar diretivas do Razor para importar namespaces e usar injeção de dependência. Diretivas compartilhadas por muitas exibições podem ser especificadas em um arquivo comum _ViewImports.cshtml . O _ViewImports arquivo dá suporte às seguintes diretivas:

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

O arquivo não dá suporte a outros Razor recursos, como funções e definições de seção.

Um arquivo de exemplo _ViewImports.cshtml :

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

O _ViewImports.cshtml arquivo de um aplicativo ASP.NET Core MVC normalmente é colocado na pasta Páginas (ou Exibições). Um _ViewImports.cshtml arquivo pode ser colocado em qualquer pasta, nesse caso, ele será aplicado apenas a páginas ou exibições dentro dessa pasta e suas subpastas. _ViewImports são processados a partir do nível raiz e, em seguida, em cada pasta até chegar à localização da própria página ou visualização. _ViewImports as configurações especificadas no nível raiz podem ser substituídas no nível da pasta.

Por exemplo, suponha:

  • O arquivo de nível _ViewImports.cshtml raiz inclui @model MyModel1 e @addTagHelper *, MyTagHelper1.
  • Um arquivo de subpasta _ViewImports.cshtml inclui @model MyModel2 e @addTagHelper *, MyTagHelper2.

Páginas e exibições na subpasta terão acesso a Auxiliares de Marca e ao modelo MyModel2.

Se vários _ViewImports.cshtml arquivos forem encontrados na hierarquia de arquivos, o comportamento combinado das diretivas será:

  • @addTagHelper, @removeTagHelper: executar tudo, em ordem.
  • @tagHelperPrefix: a imagem mais próxima da vista substitui todas as outras.
  • @model: a imagem mais próxima da vista substitui todas as outras.
  • @inherits: a imagem mais próxima da vista substitui todas as outras.
  • @using: todos estão incluídos; duplicatas são ignoradas.
  • @inject: para cada propriedade, a mais próxima da exibição substitui todas as outras com o mesmo nome de propriedade.

Executando código antes de cada exibição

O código que precisa ser executado antes de cada exibição ou página deve ser colocado no _ViewStart.cshtml arquivo. Por convenção, o _ViewStart.cshtml arquivo está localizado na pasta Páginas (ou Exibições). As instruções listadas em _ViewStart.cshtml são executadas antes de cada exibição completa (não em layouts, nem em exibições parciais). Como ViewImports.cshtml, _ViewStart.cshtml é hierárquico. Se um _ViewStart.cshtml arquivo for definido na pasta exibição ou páginas, ele será executado após o definido na raiz da pasta Páginas (ou Exibições) (se houver).

Um arquivo de exemplo _ViewStart.cshtml :

@{
    Layout = "_Layout";
}

O arquivo anterior especifica que todas as exibições usarão o _Layout.cshtml layout.

_ViewStart.cshtml e _ViewImports.cshtmlnormalmente não são colocados na pasta /Pages/Shared (ou /Views/Shared). As versões no nível do aplicativo desses arquivos devem ser colocadas diretamente na pasta /Pages (ou /Views).