Parte 3, Razor Pages gerado por scaffolding no ASP.NET Core

Observação

Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Importante

Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.

Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

De Rick Anderson

Este tutorial examina o Razor Pages criadi por scaffolding no tutorial anterior.

As páginas Create, Delete, Details e Edit

Examine o modelo do Pages/Movies/Index.cshtml.cs Page:

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies;

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

    public IList<Movie> Movie { get;set; }  = default!;

    public async Task OnGetAsync()
    {
        if (_context.Movie != null)
        {
            Movie = await _context.Movie.ToListAsync();
        }
    }
}

Razor Pages são derivados de PageModel. Por convenção, a classe derivada de PageModel é chamada de PageNameModel. Por exemplo, a página Índice é chamada de IndexModel.

O construtor usa injeção de dependência para adicionar o RazorPagesMovieContext à página:

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

Confira Código assíncrono para obter mais informações sobre a programação assíncrona com o Entity Framework.

Quando uma solicitação GET é feita à página, o método OnGetAsync retorna uma lista de filmes para o Razor Page. Em um Razor Page, OnGetAsync ou OnGet é chamado para inicializar o estado da página. Nesse caso, OnGetAsync obtém uma lista de filmes e os exibe.

Quando OnGet retorna void ou OnGetAsync retorna Task, então nenhuma instrução return é usada. Por exemplo, examine o Privacy Page:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace RazorPagesMovie.Pages
{
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

Quando o tipo de retorno for IActionResult ou Task<IActionResult>, é necessário fornecer uma instrução de retorno. Por exemplo, o método Pages/Movies/Create.cshtml.cs OnPostAsync:

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Examine o Pages/Movies/Index.cshtmlRazor Page:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

O Razor pode fazer a transição do HTML em C# ou em marcação específica do Razor. Quando um símbolo @ é seguido por uma palavra-chave reservada do Razor, ele faz a transição para marcação específica do Razor, caso contrário, ele faz a transição para C#.

A diretiva @page

A diretiva do @pageRazor transforma o arquivo em uma ação do MVC, o que significa que ele pode manipular solicitações. @page deve ser a primeira diretiva do Razor em uma página. @page e @model são exemplos de transição para a marcação específica do Razor. Confira RazorSintaxe para obter mais informações.

A diretiva @model

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

A diretiva @model especifica o tipo do modelo passado para o Razor Page. No exemplo anterior, a linha @model torna a classe derivada de PageModel disponível para o Razor Page. O modelo é usado nos auxiliares HTML@Html.DisplayNameFor e @Html.DisplayFor na página.

Examine a expressão lambda usada no auxiliar HTML a seguir:

@Html.DisplayNameFor(model => model.Movie[0].Title)

O auxiliar HTML DisplayNameFor inspeciona a propriedade Title referenciada na expressão lambda para determinar o nome de exibição. A expressão lambda é inspecionada em vez de avaliada. Isso significa que não há nenhuma violação de acesso quando model, model.Movie ou model.Movie[0] é null ou vazio. Quando a expressão lambda é avaliada, por exemplo, com @Html.DisplayFor(modelItem => item.Title), os valores de propriedade do modelo são avaliados.

A página de layout

Selecione os links do menu RazorPagesMovie, Home e Privacy. Cada página mostra o mesmo layout de menu. O layout do menu é implementado no arquivo Pages/Shared/_Layout.cshtml .

Abra e examine o arquivo Pages/Shared/_Layout.cshtml.

Os modelos de layout permitem que o layout do contêiner HTML seja:

  • Especificado em um único lugar.
  • Aplicado a várias páginas no site.

Localize a linha @RenderBody(). RenderBody é um espaço reservado em que todas as exibições específicas da página são mostradas, encapsuladas na página de layout. Por exemplo, selecione o link Privacy e a exibição Pages/Privacy.cshtml será renderizada dentro do método RenderBody.

ViewData e layout

Considere a seguinte marcação do arquivo Pages/Movies/Index.cshtml:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

A marcação realçada anteriormente é um exemplo de transição do Razor para C#. Os caracteres { e } circunscrevem um bloco de código C#.

A classe base PageModel tem uma propriedade de dicionário ViewData que pode ser usada para passar dados para uma Exibição. Adicione objetos ao dicionário ViewData usando um padrão de valor de chave. No exemplo anterior, a propriedade Title é adicionada ao dicionário ViewData.

A propriedade Title é usada no arquivo Pages/Shared/_Layout.cshtml. A marcação a seguir mostra as primeiras linhas do arquivo _Layout.cshtml.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - RazorPagesMovie</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/RazorPagesMovie.styles.css" asp-append-version="true" />

Atualizar o layout

  1. Altere o elemento <title> no arquivo Pages/Shared/_Layout.cshtml para exibir Movie, em vez de RazorPagesMovie.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>@ViewData["Title"] - Movie</title>
    
  2. Localizar o elemento de âncora a seguir no arquivo Pages/Shared/_Layout.cshtml.

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. Substitua o elemento anterior pela marcação a seguir:

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    O elemento de âncora anterior é um Auxiliar de Marcas. Nesse caso, ele é o Auxiliar de Marcas de Âncora. O atributo e valor do auxiliar de marcas asp-page="/Movies/Index" cria um link para o /Movies/IndexRazor Page. O valor do atributo asp-area está vazio e, portanto, a área não é usada no link. Confira Áreas para obter mais informações.

  4. Salve as alterações e teste o aplicativo selecionando o link RpMovie. Confira o arquivo _Layout.cshtml no GitHub caso tenha problemas.

  5. Teste os links Home, RpMovie, Criar, Editar e Excluir. Cada página define o título, que pode ser visto na guia do navegador. Quando você coloca um indicador em uma página, o título é usado para o indicador.

Observação

Talvez você não consiga inserir casas decimais ou vírgulas no campo Price. Para dar suporte à validação do jQuery para localidades de idiomas diferentes do inglês que usam uma vírgula (“,”) para um ponto decimal e formatos de data diferentes do inglês dos EUA, você deve tomar medidas para globalizar o aplicativo. Confira Problema 4076 do GitHub para obter instruções sobre como adicionar casas decimais.

A propriedade Layout é definida no arquivo Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

A marcação anterior define o arquivo de layout Pages/Shared/_Layout.cshtml para todos os arquivos do Razor na pasta Pages. Veja Layout para obter mais informações.

O modelo Criar página

Examine o modelo da página Pages/Movies/Create.cshtml.cs:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        public Movie Movie { get; set; } = default!;
        

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
          if (!ModelState.IsValid || _context.Movie == null || Movie == null)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

O método OnGet inicializa qualquer estado necessário para a página. A página Criar não tem nenhum estado para inicializar, assim, Page é retornado. Apresentamos um exemplo de inicialização de estado OnGet posteriormente no tutorial. O método Page cria um objeto PageResult que renderiza a página Create.cshtml.

A propriedade Movie usa o atributo [BindProperty] para aceitar o model binding. Quando o formulário Criar posta os valores de formulário, o runtime do ASP.NET Core associa os valores postados ao modelo Movie.

O método OnPostAsync é executado quando a página posta dados de formulário:

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Se há algum erro de modelo, o formulário é reexibido juntamente com quaisquer dados de formulário postados. A maioria dos erros de modelo podem ser capturados no lado do cliente antes do formulário ser enviado. Um exemplo de um erro de modelo é postar, para o campo de data, um valor que não pode ser convertido em uma data. A validação do lado do cliente e a validação de modelo são abordadas mais adiante no tutorial.

Se não houver erros de modelo:

  • Os dados são salvos.
  • O navegador é redirecionado para a página de índice.

Criar Razor Page

Examine o arquivo Pages/Movies/Create.cshtmlRazor Page:

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

O Visual Studio exibe as marcas a seguir em uma fonte em negrito diferente usada em auxiliares de marcações:

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Exibição de VS17 da página Create.cshtml

O elemento <form method="post"> é um auxiliar de marcas de formulário. O auxiliar de marcas de formulário inclui automaticamente um token antifalsificação.

O mecanismo de scaffolding cria marcação do Razor para cada campo no modelo, exceto a ID, semelhante ao seguinte:

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

Os auxiliares de marcas de validação (<div asp-validation-summary e <span asp-validation-for) exibem erros de validação. A validação será abordada em mais detalhes posteriormente nesta série.

O auxiliar de marcas de rótulo (<label asp-for="Movie.Title" class="control-label"></label>) gera a legenda do rótulo e o atributo [for] para a propriedade Title.

O auxiliar de marcas de entrada (<input asp-for="Movie.Title" class="form-control">) usa os atributos DataAnnotations e produz os atributos HTML necessários para validação jQuery no lado do cliente.

Para obter mais informações sobre Auxiliares de Marcas, como <form method="post">, confira Auxiliares de Marcas no ASP.NET Core.

Próximas etapas

As páginas Create, Delete, Details e Edit

Examine o modelo do Pages/Movies/Index.cshtml.cs Page:

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies;

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

    public IList<Movie> Movie { get;set; }  = default!;

    public async Task OnGetAsync()
    {
        if (_context.Movie != null)
        {
            Movie = await _context.Movie.ToListAsync();
        }
    }
}

Razor Pages são derivados de PageModel. Por convenção, a classe derivada de PageModel é chamada de PageNameModel. Por exemplo, a página Índice é chamada de IndexModel.

O construtor usa injeção de dependência para adicionar o RazorPagesMovieContext à página:

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

Confira Código assíncrono para obter mais informações sobre a programação assíncrona com o Entity Framework.

Quando uma solicitação GET é feita à página, o método OnGetAsync retorna uma lista de filmes para o Razor Page. Em um Razor Page, OnGetAsync ou OnGet é chamado para inicializar o estado da página. Nesse caso, OnGetAsync obtém uma lista de filmes e os exibe.

Quando OnGet retorna void ou OnGetAsync retorna Task, então nenhuma instrução return é usada. Por exemplo, examine o Privacy Page:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace RazorPagesMovie.Pages
{
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

Quando o tipo de retorno for IActionResult ou Task<IActionResult>, é necessário fornecer uma instrução de retorno. Por exemplo, o método Pages/Movies/Create.cshtml.cs OnPostAsync:

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Examine o Pages/Movies/Index.cshtmlRazor Page:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

O Razor pode fazer a transição do HTML em C# ou em marcação específica do Razor. Quando um símbolo @ é seguido por uma palavra-chave reservada do Razor, ele faz a transição para marcação específica do Razor, caso contrário, ele faz a transição para C#.

A diretiva @page

A diretiva do @pageRazor transforma o arquivo em uma ação do MVC, o que significa que ele pode manipular solicitações. @page deve ser a primeira diretiva do Razor em uma página. @page e @model são exemplos de transição para a marcação específica do Razor. Confira RazorSintaxe para obter mais informações.

A diretiva @model

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

A diretiva @model especifica o tipo do modelo passado para o Razor Page. No exemplo anterior, a linha @model torna a classe derivada de PageModel disponível para o Razor Page. O modelo é usado nos auxiliares HTML@Html.DisplayNameFor e @Html.DisplayFor na página.

Examine a expressão lambda usada no auxiliar HTML a seguir:

@Html.DisplayNameFor(model => model.Movie[0].Title)

O auxiliar HTML DisplayNameFor inspeciona a propriedade Title referenciada na expressão lambda para determinar o nome de exibição. A expressão lambda é inspecionada em vez de avaliada. Isso significa que não há nenhuma violação de acesso quando model, model.Movie ou model.Movie[0] é null ou vazio. Quando a expressão lambda é avaliada, por exemplo, com @Html.DisplayFor(modelItem => item.Title), os valores de propriedade do modelo são avaliados.

A página de layout

Selecione os links do menu RazorPagesMovie, Home e Privacy. Cada página mostra o mesmo layout de menu. O layout do menu é implementado no arquivo Pages/Shared/_Layout.cshtml .

Abra e examine o arquivo Pages/Shared/_Layout.cshtml.

Os modelos de layout permitem que o layout do contêiner HTML seja:

  • Especificado em um único lugar.
  • Aplicado a várias páginas no site.

Localize a linha @RenderBody(). RenderBody é um espaço reservado em que todas as exibições específicas da página são mostradas, encapsuladas na página de layout. Por exemplo, selecione o link Privacy e a exibição Pages/Privacy.cshtml será renderizada dentro do método RenderBody.

ViewData e layout

Considere a seguinte marcação do arquivo Pages/Movies/Index.cshtml:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

A marcação realçada anteriormente é um exemplo de transição do Razor para C#. Os caracteres { e } circunscrevem um bloco de código C#.

A classe base PageModel tem uma propriedade de dicionário ViewData que pode ser usada para passar dados para uma Exibição. Adicione objetos ao dicionário ViewData usando um padrão de valor de chave. No exemplo anterior, a propriedade Title é adicionada ao dicionário ViewData.

A propriedade Title é usada no arquivo Pages/Shared/_Layout.cshtml. A marcação a seguir mostra as primeiras linhas do arquivo _Layout.cshtml.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - RazorPagesMovie</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/RazorPagesMovie.styles.css" asp-append-version="true" />

A linha @*Markup removed for brevity.*@ é um comentário Razor. Ao contrário de comentários HTML <!-- -->, comentários do Razor não são enviados ao cliente. Confira Documentos da Web do MDN: Introdução ao HTML para obter mais informações.

Atualizar o layout

  1. Altere o elemento <title> no arquivo Pages/Shared/_Layout.cshtml para exibir Movie, em vez de RazorPagesMovie.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>@ViewData["Title"] - Movie</title>
    
  2. Localizar o elemento de âncora a seguir no arquivo Pages/Shared/_Layout.cshtml.

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. Substitua o elemento anterior pela marcação a seguir:

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    O elemento de âncora anterior é um Auxiliar de Marcas. Nesse caso, ele é o Auxiliar de Marcas de Âncora. O atributo e valor do auxiliar de marcas asp-page="/Movies/Index" cria um link para o /Movies/IndexRazor Page. O valor do atributo asp-area está vazio e, portanto, a área não é usada no link. Confira Áreas para obter mais informações.

  4. Salve as alterações e teste o aplicativo selecionando o link RpMovie. Confira o arquivo _Layout.cshtml no GitHub caso tenha problemas.

  5. Teste os links Home, RpMovie, Criar, Editar e Excluir. Cada página define o título, que pode ser visto na guia do navegador. Quando você coloca um indicador em uma página, o título é usado para o indicador.

Observação

Talvez você não consiga inserir casas decimais ou vírgulas no campo Price. Para dar suporte à validação do jQuery para localidades de idiomas diferentes do inglês que usam uma vírgula (“,”) para um ponto decimal e formatos de data diferentes do inglês dos EUA, você deve tomar medidas para globalizar o aplicativo. Confira Problema 4076 do GitHub para obter instruções sobre como adicionar casas decimais.

A propriedade Layout é definida no arquivo Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

A marcação anterior define o arquivo de layout Pages/Shared/_Layout.cshtml para todos os arquivos do Razor na pasta Pages. Veja Layout para obter mais informações.

O modelo Criar página

Examine o modelo da página Pages/Movies/Create.cshtml.cs:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        public Movie Movie { get; set; } = default!;
        

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
          if (!ModelState.IsValid || _context.Movie == null || Movie == null)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

O método OnGet inicializa qualquer estado necessário para a página. A página Criar não tem nenhum estado para inicializar, assim, Page é retornado. Apresentamos um exemplo de inicialização de estado OnGet posteriormente no tutorial. O método Page cria um objeto PageResult que renderiza a página Create.cshtml.

A propriedade Movie usa o atributo [BindProperty] para aceitar o model binding. Quando o formulário Criar posta os valores de formulário, o runtime do ASP.NET Core associa os valores postados ao modelo Movie.

O método OnPostAsync é executado quando a página posta dados de formulário:

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Se há algum erro de modelo, o formulário é reexibido juntamente com quaisquer dados de formulário postados. A maioria dos erros de modelo podem ser capturados no lado do cliente antes do formulário ser enviado. Um exemplo de um erro de modelo é postar, para o campo de data, um valor que não pode ser convertido em uma data. A validação do lado do cliente e a validação de modelo são abordadas mais adiante no tutorial.

Se não houver erros de modelo:

  • Os dados são salvos.
  • O navegador é redirecionado para a página de índice.

Criar Razor Page

Examine o arquivo Pages/Movies/Create.cshtmlRazor Page:

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

O Visual Studio exibe as marcas a seguir em uma fonte em negrito diferente usada em auxiliares de marcações:

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Exibição de VS17 da página Create.cshtml

O elemento <form method="post"> é um auxiliar de marcas de formulário. O auxiliar de marcas de formulário inclui automaticamente um token antifalsificação.

O mecanismo de scaffolding cria marcação do Razor para cada campo no modelo, exceto a ID, semelhante ao seguinte:

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

Os auxiliares de marcas de validação (<div asp-validation-summary e <span asp-validation-for) exibem erros de validação. A validação será abordada em mais detalhes posteriormente nesta série.

O auxiliar de marcas de rótulo (<label asp-for="Movie.Title" class="control-label"></label>) gera a legenda do rótulo e o atributo [for] para a propriedade Title.

O auxiliar de marcas de entrada (<input asp-for="Movie.Title" class="form-control">) usa os atributos DataAnnotations e produz os atributos HTML necessários para validação jQuery no lado do cliente.

Para obter mais informações sobre Auxiliares de Marcas, como <form method="post">, confira Auxiliares de Marcas no ASP.NET Core.

Próximas etapas

As páginas Create, Delete, Details e Edit

Examine o modelo do Pages/Movies/Index.cshtml.cs Page:

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class IndexModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IList<Movie> Movie { get;set; } = default!;

        public async Task OnGetAsync()
        {
            if (_context.Movie != null)
            {
                Movie = await _context.Movie.ToListAsync();
            }
        }
    }
}

Razor Pages são derivados de PageModel. Por convenção, a classe derivada de PageModel é chamada de PageNameModel. Por exemplo, a página Índice é chamada de IndexModel.

O construtor usa injeção de dependência para adicionar o RazorPagesMovieContext à página:

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

Confira Código assíncrono para obter mais informações sobre a programação assíncrona com o Entity Framework.

Quando uma solicitação é feita à página, o método OnGetAsync retorna uma lista de filmes para o Razor Page. Em um Razor Page, OnGetAsync ou OnGet é chamado para inicializar o estado da página. Nesse caso, OnGetAsync obtém uma lista de filmes e os exibe.

Quando OnGet retorna void ou OnGetAsync retorna Task, então nenhuma instrução return é usada. Por exemplo, examine o Privacy Page:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace RazorPagesMovie.Pages
{
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

Quando o tipo de retorno for IActionResult ou Task<IActionResult>, é necessário fornecer uma instrução de retorno. Por exemplo, o método Pages/Movies/Create.cshtml.csOnPostAsync:

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid || _context.Movie == null || Movie == null)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Examine o Pages/Movies/Index.cshtmlRazor Page:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

O Razor pode fazer a transição do HTML em C# ou em marcação específica do Razor. Quando um símbolo @ é seguido por uma palavra-chave reservada do Razor, ele faz a transição para marcação específica do Razor, caso contrário, ele faz a transição para C#.

A diretiva @page

A diretiva do @pageRazor transforma o arquivo em uma ação do MVC, o que significa que ele pode manipular solicitações. @page deve ser a primeira diretiva do Razor em uma página. @page e @model são exemplos de transição para a marcação específica do Razor. Confira RazorSintaxe para obter mais informações.

A diretiva @model

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

A diretiva @model especifica o tipo do modelo passado para o Razor Page. No exemplo anterior, a linha @model torna a classe derivada de PageModel disponível para o Razor Page. O modelo é usado nos auxiliares HTML@Html.DisplayNameFor e @Html.DisplayFor na página.

Examine a expressão lambda usada no auxiliar HTML a seguir:

@Html.DisplayNameFor(model => model.Movie[0].Title)

O auxiliar HTML DisplayNameFor inspeciona a propriedade Title referenciada na expressão lambda para determinar o nome de exibição. A expressão lambda é inspecionada em vez de avaliada. Isso significa que não há nenhuma violação de acesso quando model, model.Movie ou model.Movie[0] é null ou vazio. Quando a expressão lambda é avaliada, por exemplo, com @Html.DisplayFor(modelItem => item.Title), os valores de propriedade do modelo são avaliados.

A página de layout

Selecione os links do menu RazorPagesMovie, Home e Privacy. Cada página mostra o mesmo layout de menu. O layout do menu é implementado no arquivo Pages/Shared/_Layout.cshtml .

Abra e examine o arquivo Pages/Shared/_Layout.cshtml.

Os modelos de layout permitem que o layout do contêiner HTML seja:

  • Especificado em um único lugar.
  • Aplicado a várias páginas no site.

Localize a linha @RenderBody(). RenderBody é um espaço reservado em que todas as exibições específicas da página são mostradas, encapsuladas na página de layout. Por exemplo, selecione o link Privacy e a exibição Pages/Privacy.cshtml será renderizada dentro do método RenderBody.

ViewData e layout

Considere a seguinte marcação do arquivo Pages/Movies/Index.cshtml:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

A marcação realçada anteriormente é um exemplo de transição do Razor para C#. Os caracteres { e } circunscrevem um bloco de código C#.

A classe base PageModel tem uma propriedade de dicionário ViewData que pode ser usada para passar dados para uma Exibição. Adicione objetos ao dicionário ViewData usando um padrão de valor de chave. No exemplo anterior, a propriedade Title é adicionada ao dicionário ViewData.

A propriedade Title é usada no arquivo Pages/Shared/_Layout.cshtml. A marcação a seguir mostra as primeiras linhas do arquivo _Layout.cshtml.

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

     @*Markup removed for brevity.*@
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />

A linha @*Markup removed for brevity.*@ é um comentário Razor. Ao contrário de comentários HTML <!-- -->, comentários do Razor não são enviados ao cliente. Confira Documentos da Web do MDN: Introdução ao HTML para obter mais informações.

Atualizar o layout

  1. Altere o elemento <title> no arquivo Pages/Shared/_Layout.cshtml para exibir Movie, em vez de RazorPagesMovie.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>@ViewData["Title"] - Movie</title>
    
  2. Localizar o elemento de âncora a seguir no arquivo Pages/Shared/_Layout.cshtml.

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. Substitua o elemento anterior pela marcação a seguir:

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    O elemento de âncora anterior é um Auxiliar de Marcas. Nesse caso, ele é o Auxiliar de Marcas de Âncora. O atributo e valor do auxiliar de marcas asp-page="/Movies/Index" cria um link para o /Movies/IndexRazor Page. O valor do atributo asp-area está vazio e, portanto, a área não é usada no link. Confira Áreas para obter mais informações.

  4. Salve as alterações e teste o aplicativo selecionando o link RpMovie. Confira o arquivo _Layout.cshtml no GitHub caso tenha problemas.

  5. Teste os links Home, RpMovie, Criar, Editar e Excluir. Cada página define o título, que pode ser visto na guia do navegador. Quando você coloca um indicador em uma página, o título é usado para o indicador.

Observação

Talvez você não consiga inserir casas decimais ou vírgulas no campo Price. Para dar suporte à validação do jQuery para localidades de idiomas diferentes do inglês que usam uma vírgula (“,”) para um ponto decimal e formatos de data diferentes do inglês dos EUA, você deve tomar medidas para globalizar o aplicativo. Confira Problema 4076 do GitHub para obter instruções sobre como adicionar casas decimais.

A propriedade Layout é definida no arquivo Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

A marcação anterior define o arquivo de layout Pages/Shared/_Layout.cshtml para todos os arquivos do Razor na pasta Pages. Veja Layout para obter mais informações.

O modelo Criar página

Examine o modelo da página Pages/Movies/Create.cshtml.cs:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        public Movie Movie { get; set; } = default!;
        

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
          if (!ModelState.IsValid || _context.Movie == null || Movie == null)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

O método OnGet inicializa qualquer estado necessário para a página. A página Criar não tem nenhum estado para inicializar, assim, Page é retornado. Apresentamos um exemplo de inicialização de estado OnGet posteriormente no tutorial. O método Page cria um objeto PageResult que renderiza a página Create.cshtml.

A propriedade Movie usa o atributo [BindProperty] para aceitar o model binding. Quando o formulário Criar posta os valores de formulário, o runtime do ASP.NET Core associa os valores postados ao modelo Movie.

O método OnPostAsync é executado quando a página posta dados de formulário:

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid || _context.Movie == null || Movie == null)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Se há algum erro de modelo, o formulário é reexibido juntamente com quaisquer dados de formulário postados. A maioria dos erros de modelo podem ser capturados no lado do cliente antes do formulário ser enviado. Um exemplo de um erro de modelo é postar, para o campo de data, um valor que não pode ser convertido em uma data. A validação do lado do cliente e a validação de modelo são abordadas mais adiante no tutorial.

Se não houver erros de modelo:

  • Os dados são salvos.
  • O navegador é redirecionado para a página de índice.

Criar Razor Page

Examine o arquivo Pages/Movies/Create.cshtmlRazor Page:

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

O Visual Studio exibe as marcas a seguir em uma fonte em negrito diferente usada em auxiliares de marcações:

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Exibição de VS17 da página Create.cshtml

O elemento <form method="post"> é um auxiliar de marcas de formulário. O auxiliar de marcas de formulário inclui automaticamente um token antifalsificação.

O mecanismo de scaffolding cria marcação do Razor para cada campo no modelo, exceto a ID, semelhante ao seguinte:

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

Os auxiliares de marcas de validação (<div asp-validation-summary e <span asp-validation-for) exibem erros de validação. A validação será abordada em mais detalhes posteriormente nesta série.

O auxiliar de marcas de rótulo (<label asp-for="Movie.Title" class="control-label"></label>) gera a legenda do rótulo e o atributo [for] para a propriedade Title.

O auxiliar de marcas de entrada (<input asp-for="Movie.Title" class="form-control">) usa os atributos DataAnnotations e produz os atributos HTML necessários para validação jQuery no lado do cliente.

Para obter mais informações sobre Auxiliares de Marcas, como <form method="post">, confira Auxiliares de Marcas no ASP.NET Core.

Próximas etapas

As páginas Create, Delete, Details e Edit

Examine o modelo do Pages/Movies/Index.cshtml.cs Page:

// Unused usings removed.
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace RazorPagesMovie.Pages.Movies
{
    public class IndexModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }
        public IList<Movie> Movie { get;set; }

        public async Task OnGetAsync()
        {
            Movie = await _context.Movie.ToListAsync();
        }
    }
}

Razor Pages são derivados de PageModel. Por convenção, a classe derivada de PageModel é chamada de <PageName>Model. O construtor usa injeção de dependência para adicionar o RazorPagesMovieContext à página:

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

Confira Código assíncrono para obter mais informações sobre a programação assíncrona com o Entity Framework.

Quando uma solicitação é feita à página, o método OnGetAsync retorna uma lista de filmes para o Razor Page. Em um Razor Page, OnGetAsync ou OnGet é chamado para inicializar o estado da página. Nesse caso, OnGetAsync obtém uma lista de filmes e os exibe.

Quando OnGet retorna void ou OnGetAsync retorna Task, então nenhuma instrução return é usada. Por exemplo, o Privacy Page:

public class PrivacyModel : PageModel
{
    private readonly ILogger<PrivacyModel> _logger;

    public PrivacyModel(ILogger<PrivacyModel> logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
    }
}

Quando o tipo de retorno for IActionResult ou Task<IActionResult>, é necessário fornecer uma instrução de retorno. Por exemplo, o método Pages/Movies/Create.cshtml.csOnPostAsync:

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _context.Movie.Add(Movie);
        await _context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

Examine o Pages/Movies/Index.cshtmlRazor Page:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

O Razor pode fazer a transição do HTML em C# ou em marcação específica do Razor. Quando um símbolo @ é seguido por uma palavra-chave reservada do Razor, ele faz a transição para marcação específica do Razor, caso contrário, ele faz a transição para C#.

A diretiva @page

A diretiva do @pageRazor transforma o arquivo em uma ação do MVC, o que significa que ele pode manipular solicitações. @page deve ser a primeira diretiva do Razor em uma página. @page e @model são exemplos de transição para a marcação específica do Razor. Confira RazorSintaxe para obter mais informações.

A diretiva @model

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

A diretiva @model especifica o tipo do modelo passado para o Razor Page. No exemplo anterior, a linha @model torna a classe derivada de PageModel disponível para o Razor Page. O modelo é usado nos auxiliares HTML@Html.DisplayNameFor e @Html.DisplayFor na página.

Examine a expressão lambda usada no auxiliar HTML a seguir:

@Html.DisplayNameFor(model => model.Movie[0].Title)

O auxiliar HTML DisplayNameFor inspeciona a propriedade Title referenciada na expressão lambda para determinar o nome de exibição. A expressão lambda é inspecionada em vez de avaliada. Isso significa que não há nenhuma violação de acesso quando model, model.Movie ou model.Movie[0] é null ou vazio. Quando a expressão lambda é avaliada, por exemplo, com @Html.DisplayFor(modelItem => item.Title), os valores de propriedade do modelo são avaliados.

A página de layout

Selecione os links do menu RazorPagesMovie, Home e Privacy. Cada página mostra o mesmo layout de menu. O layout do menu é implementado no arquivo Pages/Shared/_Layout.cshtml .

Abra e examine o arquivo Pages/Shared/_Layout.cshtml.

Os modelos de layout permitem que o layout do contêiner HTML seja:

  • Especificado em um único lugar.
  • Aplicado a várias páginas no site.

Localize a linha @RenderBody(). RenderBody é um espaço reservado em que todas as exibições específicas da página são mostradas, encapsuladas na página de layout. Por exemplo, selecione o link Privacy e a exibição Pages/Privacy.cshtml será renderizada dentro do método RenderBody.

ViewData e layout

Considere a seguinte marcação do arquivo Pages/Movies/Index.cshtml:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@{
    ViewData["Title"] = "Index";
}

A marcação realçada anteriormente é um exemplo de transição do Razor para C#. Os caracteres { e } circunscrevem um bloco de código C#.

A classe base PageModel tem uma propriedade de dicionário ViewData que pode ser usada para passar dados para uma Exibição. Adicione objetos ao dicionário ViewData usando um padrão de valor de chave. No exemplo anterior, a propriedade Title é adicionada ao dicionário ViewData.

A propriedade Title é usada no arquivo Pages/Shared/_Layout.cshtml. A marcação a seguir mostra as primeiras linhas do arquivo _Layout.cshtml.

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

    @*Markup removed for brevity.*@

A linha @*Markup removed for brevity.*@ é um comentário Razor. Ao contrário de comentários HTML <!-- -->, comentários do Razor não são enviados ao cliente. Confira Documentos da Web do MDN: Introdução ao HTML para obter mais informações.

Atualizar o layout

  1. Altere o elemento <title> no arquivo Pages/Shared/_Layout.cshtml para exibir Movie, em vez de RazorPagesMovie.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>@ViewData["Title"] - Movie</title>
    
  2. Localizar o elemento de âncora a seguir no arquivo Pages/Shared/_Layout.cshtml.

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. Substitua o elemento anterior pela marcação a seguir:

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    O elemento de âncora anterior é um Auxiliar de Marcas. Nesse caso, ele é o Auxiliar de Marcas de Âncora. O atributo e valor do auxiliar de marcas asp-page="/Movies/Index" cria um link para o /Movies/IndexRazor Page. O valor do atributo asp-area está vazio e, portanto, a área não é usada no link. Confira Áreas para obter mais informações.

  4. Salve as alterações e teste o aplicativo selecionando o link RpMovie. Confira o arquivo _Layout.cshtml no GitHub caso tenha problemas.

  5. Teste os links Home, RpMovie, Criar, Editar e Excluir. Cada página define o título, que pode ser visto na guia do navegador. Quando você coloca um indicador em uma página, o título é usado para o indicador.

Observação

Talvez você não consiga inserir casas decimais ou vírgulas no campo Price. Para dar suporte à validação do jQuery para localidades de idiomas diferentes do inglês que usam uma vírgula (“,”) para um ponto decimal e formatos de data diferentes do inglês dos EUA, você deve tomar medidas para globalizar o aplicativo. Confira Problema 4076 do GitHub para obter instruções sobre como adicionar casas decimais.

A propriedade Layout é definida no arquivo Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

A marcação anterior define o arquivo de layout Pages/Shared/_Layout.cshtml para todos os arquivos do Razor na pasta Pages. Veja Layout para obter mais informações.

O modelo Criar página

Examine o modelo da página Pages/Movies/Create.cshtml.cs:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesMovie.Models;
using System;
using System.Threading.Tasks;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        public Movie Movie { get; set; }

        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

O método OnGet inicializa qualquer estado necessário para a página. A página Criar não tem nenhum estado para inicializar, assim, Page é retornado. Apresentamos um exemplo de inicialização de estado OnGet posteriormente no tutorial. O método Page cria um objeto PageResult que renderiza a página Create.cshtml.

A propriedade Movie usa o atributo [BindProperty] para aceitar o model binding. Quando o formulário Criar posta os valores de formulário, o runtime do ASP.NET Core associa os valores postados ao modelo Movie.

O método OnPostAsync é executado quando a página posta dados de formulário:

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Se há algum erro de modelo, o formulário é reexibido juntamente com quaisquer dados de formulário postados. A maioria dos erros de modelo podem ser capturados no lado do cliente antes do formulário ser enviado. Um exemplo de um erro de modelo é postar, para o campo de data, um valor que não pode ser convertido em uma data. A validação do lado do cliente e a validação de modelo são abordadas mais adiante no tutorial.

Se não houver erros de modelo:

  • Os dados são salvos.
  • O navegador é redirecionado para a página de índice.

Criar Razor Page

Examine o arquivo Pages/Movies/Create.cshtmlRazor Page:

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

O Visual Studio exibe as marcas a seguir em uma fonte em negrito diferente usada em auxiliares de marcações:

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Exibição de VS17 da página Create.cshtml

O elemento <form method="post"> é um auxiliar de marcas de formulário. O auxiliar de marcas de formulário inclui automaticamente um token antifalsificação.

O mecanismo de scaffolding cria marcação do Razor para cada campo no modelo, exceto a ID, semelhante ao seguinte:

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

Os auxiliares de marcas de validação (<div asp-validation-summary e <span asp-validation-for) exibem erros de validação. A validação será abordada em mais detalhes posteriormente nesta série.

O auxiliar de marcas de rótulo (<label asp-for="Movie.Title" class="control-label"></label>) gera a legenda do rótulo e o atributo [for] para a propriedade Title.

O auxiliar de marcas de entrada (<input asp-for="Movie.Title" class="form-control">) usa os atributos DataAnnotations e produz os atributos HTML necessários para validação jQuery no lado do cliente.

Para obter mais informações sobre Auxiliares de Marcas, como <form method="post">, confira Auxiliares de Marcas no ASP.NET Core.

Próximas etapas