Parte 3. Razor Pages con scaffolding en ASP.NET Core
Nota:
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Advertencia
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulta la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulta la versión .NET 8 de este artículo.
Importante
Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.
Para la versión actual, consulte la versión de .NET 9 de este artículo.
Por Rick Anderson
En este tutorial se examinan las instancias de Razor Pages creadas con la técnica scaffolding en el tutorial anterior.
Páginas de creación, eliminación, detalles y edición
Examine el modelo de Pages/Movies/Index.cshtml.cs
Pages:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Data;
using RazorPagesMovie.Models;
using Microsoft.AspNetCore.Mvc.Rendering;
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()
{
Movie = await _context.Movie.ToListAsync();
}
}
}
Las instancias de Razor Pages derivan de PageModel. Por convención, la clase derivada de PageModel
se denomina PageNameModel
. Por ejemplo, la página Index se denomina IndexModel
.
El constructor aplica la inserción de dependencias para agregar RazorPagesMovieContext
a la página:
public class IndexModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
Vea Código asincrónico para obtener más información sobre programación asincrónica con Entity Framework.
Cuando se efectúa una solicitud GET
de la página, el método OnGetAsync
devuelve una lista de películas a la instancia Razor de Page. En la instancia de Razor Pages, se llama a OnGetAsync
o OnGet
para inicializar el estado de la página. En este caso, OnGetAsync
obtiene una lista de películas y las muestra.
Cuando OnGet
devuelve void
o OnGetAsync
devuelve Task
, no se utiliza ninguna instrucción de devolución. Por ejemplo, examine la página Privacy:
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()
{
}
}
}
Cuando el tipo de valor devuelto es IActionResult o Task<IActionResult>
, se debe proporcionar una instrucción return. Por ejemplo, la llamada al método de 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 la instancia de Razor Pages Pages/Movies/Index.cshtml
:
@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>
Razor puede realizar la transición de HTML a C# o a un marcado específico de Razor. Cuando el símbolo @
va seguido de una palabra clave reservada de Razor, se realiza una transición a un marcado específico de Razor; en caso contrario, la transición se realiza a C#.
La directiva @page
La directiva @page
de Razor convierte el archivo en una acción de MVC, lo que significa que puede administrar las solicitudes. @page
debe ser la primera directiva de Razor de una página. @page
y @model
son ejemplos de la transición a un marcado específico de Razor. Vea Sintaxis de Razor para obtener más información.
La directiva @model
@page
@model RazorPagesMovie.Pages.Movies.IndexModel
La directiva @model
especifica el tipo del modelo que se pasa a una instancia de Razor Pages. En el ejemplo anterior, la línea @model
permite que la clase derivada de PageModel
esté disponible en la instancia de Razor Pages. El modelo se usa en los asistentes de HTML @Html.DisplayNameFor
y @Html.DisplayFor
de la página.
Examine la expresión lambda usada en el siguiente asistente de HTML:
@Html.DisplayNameFor(model => model.Movie[0].Title)
El asistente de HTML DisplayNameFor inspecciona la propiedad Title
a la que se hace referencia en la expresión lambda para determinar el nombre para mostrar. La expresión lambda se inspecciona, no se evalúa. Esto significa que no hay ninguna infracción de acceso si model
, model.Movie
o model.Movie[0]
son null
o están vacíos. Al evaluar la expresión lambda (por ejemplo, con @Html.DisplayFor(modelItem => item.Title)
), se evalúan los valores de propiedad del modelo.
Página de diseño
Selecciona los vínculos de menú RazorPagesMovie, Home y Privacy. Cada página muestra el mismo diseño de menú. El diseño de menú se implementa en el archivo Pages/Shared/_Layout.cshtml
.
Abra y examine el archivo Pages/Shared/_Layout.cshtml
.
Las plantillas de diseño permiten que el diseño del contenedor HTML:
- Esté especificado en un solo lugr.
- Se aplique en varias páginas del sitio.
Busque la línea @RenderBody()
. RenderBody
es un marcador de posición donde se muestran todas las vistas específicas de página, encapsuladas en la página de diseño. Por ejemplo, seleccione el vínculo Privacy y la vista Pages/Privacy.cshtml
se representa dentro del método RenderBody
.
Propiedades ViewData y Layout
Tenga en cuenta el siguiente marcado del archivo Pages/Movies/Index.cshtml
:
@page
@model RazorPagesMovie.Pages.Movies.IndexModel
@{
ViewData["Title"] = "Index";
}
El marcado resaltado anterior es un ejemplo de Razor con una transición a C#. Los caracteres {
y }
delimitan un bloque de código de C#.
La clase base PageModel
contiene una propiedad de diccionario ViewData
que se puede usar para pasar datos a una vista. Los objetos se agregan al diccionario ViewData
con un patrón clave-valor. En el ejemplo anterior, la propiedad Title
se agrega al diccionario ViewData
.
La propiedad Title
se usa en el archivo Pages/Shared/_Layout.cshtml
. En el siguiente marcado se muestran las primeras líneas del archivo _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.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/RazorPagesMovie.styles.css" asp-append-version="true" />
Actualizar el diseño
Cambie el elemento
<title>
del archivoPages/Shared/_Layout.cshtml
para mostrar Movie en lugar 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>
Busque el siguiente elemento delimitador en el archivo
Pages/Shared/_Layout.cshtml
.<a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
Reemplace el elemento anterior por el marcado siguiente:
<a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
El elemento delimitador anterior es un asistente de etiquetas. En este caso, se trata de el asistente de etiquetas Anchor. El atributo y valor del asistente de etiquetas
asp-page="/Movies/Index"
crea un vínculo a/Movies/Index
en Razor Pages. El valor de atributoasp-area
está vacío, por lo que no se usa el área del vínculo. Consulte Áreas para obtener más información.Guarde los cambios y pruebe la aplicación seleccionando el vínculo RpMovie (Película de RP). Si tiene cualquier problema, consulte el archivo _Layout.cshtml en GitHub.
Pruebe los vínculos Home, RpMovie, Create, Edit y Delete. Cada página establece el título, que puede ver en la pestaña del explorador. Al marcar una página, se usa el título para el marcador.
Nota
Es posible que no pueda escribir comas decimales en el campo Price
. Para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos, debe seguir unos pasos para globalizar la aplicación. Consulte este problema 4076 de GitHub para instrucciones sobre cómo agregar la coma decimal.
La propiedad Layout
se establece en el archivo Pages/_ViewStart.cshtml
:
@{
Layout = "_Layout";
}
El marcado anterior establece el archivo de diseño de todos los archivos de Razor de la carpeta Pages en Pages/Shared/_Layout.cshtml
. Vea Layout (Diseño) para más información.
Modelo de página Crear
Examine el modelo de página Pages/Movies/Create.cshtml.cs
:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using RazorPagesMovie.Data;
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)
{
return Page();
}
_context.Movie.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
}
El método OnGet
inicializa cualquier estado necesario para la página. La página Crear no tiene ningún estado que inicializar, de modo que se devuelve Page
. Más adelante en el tutorial se muestra un ejemplo del estado de inicialización de OnGet
. El método Page
crea un objeto PageResult
que representa la página Create.cshtml
.
La propiedad Movie
usa el atributo [BindProperty] para participar en el enlace de modelos. Cuando el formulario de creación publica los valores del formulario, el tiempo de ejecución de ASP.NET Core enlaza los valores publicados con el modelo Movie
.
El método OnPostAsync
se ejecuta cuando la página publica los datos del formulario:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Movie.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Si hay algún error de modelo, se vuelve a mostrar el formulario, junto con los datos del formulario publicados. La mayoría de los errores de modelo se pueden capturar en el cliente antes de que se publique el formulario. Un ejemplo de un error de modelo consiste en publicar un valor para el campo de fecha que no se puede convertir en una fecha. Más adelante en el tutorial, hablaremos de la validación del lado cliente y de la validación de modelos.
Si no hay ningún error de modelo:
- Los datos se guardan.
- El explorador se redirige a la página Index.
La página de creación de instancias de Razor Pages
Examine el archivo de instancia de Razor Pages Pages/Movies/Create.cshtml
:
@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");}
}
Visual Studio muestra las etiquetas siguientes con una fuente negrita diferenciada que se aplica a los asistentes de etiquetas:
<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>
El elemento <form method="post">
es un asistente de etiquetas de formulario. El asistente de etiquetas de formulario incluye automáticamente un token antifalsificación.
El motor de scaffolding crea un marcado de Razor para cada campo del modelo (excepto el identificador) similar al siguiente:
<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>
Los asistentes de etiquetas de validación (<div asp-validation-summary
y <span asp-validation-for
) muestran errores de validación. La validación se trata con más detalle en un punto posterior de esta serie.
El asistente de etiquetas (<label asp-for="Movie.Title" class="control-label"></label>
) genera el título de la etiqueta y el atributo [for]
para la propiedad Title
.
El asistente de etiquetas de entrada (<input asp-for="Movie.Title" class="form-control">
) usa los atributos DataAnnotations y genera los atributos HTML necesarios para la validación de jQuery en el lado del cliente.
Para obtener más información sobre los asistentes de etiquetas como <form method="post">
, consulte Asistentes de etiquetas en ASP.NET Core.
Pasos siguientes
Páginas de creación, eliminación, detalles y edición
Examine el modelo de Pages/Movies/Index.cshtml.cs
Pages:
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();
}
}
}
Las instancias de Razor Pages derivan de PageModel. Por convención, la clase derivada de PageModel
se denomina PageNameModel
. Por ejemplo, la página Index se denomina IndexModel
.
El constructor aplica la inserción de dependencias para agregar RazorPagesMovieContext
a la página:
public class IndexModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
Vea Código asincrónico para obtener más información sobre programación asincrónica con Entity Framework.
Cuando se efectúa una solicitud GET
de la página, el método OnGetAsync
devuelve una lista de películas a la instancia Razor de Page. En la instancia de Razor Pages, se llama a OnGetAsync
o OnGet
para inicializar el estado de la página. En este caso, OnGetAsync
obtiene una lista de películas y las muestra.
Cuando OnGet
devuelve void
o OnGetAsync
devuelve Task
, no se utiliza ninguna instrucción de devolución. Por ejemplo, examine la página Privacy:
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()
{
}
}
}
Cuando el tipo de valor devuelto es IActionResult o Task<IActionResult>
, se debe proporcionar una instrucción return. Por ejemplo, la llamada al método de 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 la instancia de Razor Pages Pages/Movies/Index.cshtml
:
@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>
Razor puede realizar la transición de HTML a C# o a un marcado específico de Razor. Cuando el símbolo @
va seguido de una palabra clave reservada de Razor, se realiza una transición a un marcado específico de Razor; en caso contrario, la transición se realiza a C#.
La directiva @page
La directiva @page
de Razor convierte el archivo en una acción de MVC, lo que significa que puede administrar las solicitudes. @page
debe ser la primera directiva de Razor de una página. @page
y @model
son ejemplos de la transición a un marcado específico de Razor. Vea Sintaxis de Razor para obtener más información.
La directiva @model
@page
@model RazorPagesMovie.Pages.Movies.IndexModel
La directiva @model
especifica el tipo del modelo que se pasa a una instancia de Razor Pages. En el ejemplo anterior, la línea @model
permite que la clase derivada de PageModel
esté disponible en la instancia de Razor Pages. El modelo se usa en los asistentes de HTML @Html.DisplayNameFor
y @Html.DisplayFor
de la página.
Examine la expresión lambda usada en el siguiente asistente de HTML:
@Html.DisplayNameFor(model => model.Movie[0].Title)
El asistente de HTML DisplayNameFor inspecciona la propiedad Title
a la que se hace referencia en la expresión lambda para determinar el nombre para mostrar. La expresión lambda se inspecciona, no se evalúa. Esto significa que no hay ninguna infracción de acceso si model
, model.Movie
o model.Movie[0]
son null
o están vacíos. Al evaluar la expresión lambda (por ejemplo, con @Html.DisplayFor(modelItem => item.Title)
), se evalúan los valores de propiedad del modelo.
Página de diseño
Selecciona los vínculos de menú RazorPagesMovie, Home y Privacy. Cada página muestra el mismo diseño de menú. El diseño de menú se implementa en el archivo Pages/Shared/_Layout.cshtml
.
Abra y examine el archivo Pages/Shared/_Layout.cshtml
.
Las plantillas de diseño permiten que el diseño del contenedor HTML:
- Esté especificado en un solo lugr.
- Se aplique en varias páginas del sitio.
Busque la línea @RenderBody()
. RenderBody
es un marcador de posición donde se muestran todas las vistas específicas de página, encapsuladas en la página de diseño. Por ejemplo, seleccione el vínculo Privacy y la vista Pages/Privacy.cshtml
se representa dentro del método RenderBody
.
Propiedades ViewData y Layout
Tenga en cuenta el siguiente marcado del archivo Pages/Movies/Index.cshtml
:
@page
@model RazorPagesMovie.Pages.Movies.IndexModel
@{
ViewData["Title"] = "Index";
}
El marcado resaltado anterior es un ejemplo de Razor con una transición a C#. Los caracteres {
y }
delimitan un bloque de código de C#.
La clase base PageModel
contiene una propiedad de diccionario ViewData
que se puede usar para pasar datos a una vista. Los objetos se agregan al diccionario ViewData
con un patrón clave-valor. En el ejemplo anterior, la propiedad Title
se agrega al diccionario ViewData
.
La propiedad Title
se usa en el archivo Pages/Shared/_Layout.cshtml
. En el siguiente marcado se muestran las primeras líneas del archivo _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" />
Actualizar el diseño
Cambie el elemento
<title>
del archivoPages/Shared/_Layout.cshtml
para mostrar Movie en lugar 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>
Busque el siguiente elemento delimitador en el archivo
Pages/Shared/_Layout.cshtml
.<a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
Reemplace el elemento anterior por el marcado siguiente:
<a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
El elemento delimitador anterior es un asistente de etiquetas. En este caso, se trata de el asistente de etiquetas Anchor. El atributo y valor del asistente de etiquetas
asp-page="/Movies/Index"
crea un vínculo a/Movies/Index
en Razor Pages. El valor de atributoasp-area
está vacío, por lo que no se usa el área del vínculo. Consulte Áreas para obtener más información.Guarde los cambios y pruebe la aplicación seleccionando el vínculo RpMovie (Película de RP). Si tiene cualquier problema, consulte el archivo _Layout.cshtml en GitHub.
Pruebe los vínculos Home, RpMovie, Create, Edit y Delete. Cada página establece el título, que puede ver en la pestaña del explorador. Al marcar una página, se usa el título para el marcador.
Nota
Es posible que no pueda escribir comas decimales en el campo Price
. Para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos, debe seguir unos pasos para globalizar la aplicación. Consulte este problema 4076 de GitHub para instrucciones sobre cómo agregar la coma decimal.
La propiedad Layout
se establece en el archivo Pages/_ViewStart.cshtml
:
@{
Layout = "_Layout";
}
El marcado anterior establece el archivo de diseño de todos los archivos de Razor de la carpeta Pages en Pages/Shared/_Layout.cshtml
. Vea Layout (Diseño) para más información.
Modelo de página Crear
Examine el modelo de 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");
}
}
}
El método OnGet
inicializa cualquier estado necesario para la página. La página Crear no tiene ningún estado que inicializar, de modo que se devuelve Page
. Más adelante en el tutorial se muestra un ejemplo del estado de inicialización de OnGet
. El método Page
crea un objeto PageResult
que representa la página Create.cshtml
.
La propiedad Movie
usa el atributo [BindProperty] para participar en el enlace de modelos. Cuando el formulario de creación publica los valores del formulario, el tiempo de ejecución de ASP.NET Core enlaza los valores publicados con el modelo Movie
.
El método OnPostAsync
se ejecuta cuando la página publica los datos del formulario:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Movie.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Si hay algún error de modelo, se vuelve a mostrar el formulario, junto con los datos del formulario publicados. La mayoría de los errores de modelo se pueden capturar en el cliente antes de que se publique el formulario. Un ejemplo de un error de modelo consiste en publicar un valor para el campo de fecha que no se puede convertir en una fecha. Más adelante en el tutorial, hablaremos de la validación del lado cliente y de la validación de modelos.
Si no hay ningún error de modelo:
- Los datos se guardan.
- El explorador se redirige a la página Index.
La página de creación de instancias de Razor Pages
Examine el archivo de instancia de Razor Pages Pages/Movies/Create.cshtml
:
@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");}
}
Visual Studio muestra las etiquetas siguientes con una fuente negrita diferenciada que se aplica a los asistentes de etiquetas:
<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>
El elemento <form method="post">
es un asistente de etiquetas de formulario. El asistente de etiquetas de formulario incluye automáticamente un token antifalsificación.
El motor de scaffolding crea un marcado de Razor para cada campo del modelo (excepto el identificador) similar al siguiente:
<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>
Los asistentes de etiquetas de validación (<div asp-validation-summary
y <span asp-validation-for
) muestran errores de validación. La validación se trata con más detalle en un punto posterior de esta serie.
El asistente de etiquetas (<label asp-for="Movie.Title" class="control-label"></label>
) genera el título de la etiqueta y el atributo [for]
para la propiedad Title
.
El asistente de etiquetas de entrada (<input asp-for="Movie.Title" class="form-control">
) usa los atributos DataAnnotations y genera los atributos HTML necesarios para la validación de jQuery en el lado del cliente.
Para obtener más información sobre los asistentes de etiquetas como <form method="post">
, consulte Asistentes de etiquetas en ASP.NET Core.
Pasos siguientes
Páginas de creación, eliminación, detalles y edición
Examine el modelo de Pages/Movies/Index.cshtml.cs
Pages:
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();
}
}
}
Las instancias de Razor Pages derivan de PageModel. Por convención, la clase derivada de PageModel
se denomina PageNameModel
. Por ejemplo, la página Index se denomina IndexModel
.
El constructor aplica la inserción de dependencias para agregar RazorPagesMovieContext
a la página:
public class IndexModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
Vea Código asincrónico para obtener más información sobre programación asincrónica con Entity Framework.
Cuando se efectúa una solicitud GET
de la página, el método OnGetAsync
devuelve una lista de películas a la instancia Razor de Page. En la instancia de Razor Pages, se llama a OnGetAsync
o OnGet
para inicializar el estado de la página. En este caso, OnGetAsync
obtiene una lista de películas y las muestra.
Cuando OnGet
devuelve void
o OnGetAsync
devuelve Task
, no se utiliza ninguna instrucción de devolución. Por ejemplo, examine la página Privacy:
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()
{
}
}
}
Cuando el tipo de valor devuelto es IActionResult o Task<IActionResult>
, se debe proporcionar una instrucción return. Por ejemplo, la llamada al método de 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 la instancia de Razor Pages Pages/Movies/Index.cshtml
:
@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>
Razor puede realizar la transición de HTML a C# o a un marcado específico de Razor. Cuando el símbolo @
va seguido de una palabra clave reservada de Razor, se realiza una transición a un marcado específico de Razor; en caso contrario, la transición se realiza a C#.
La directiva @page
La directiva @page
de Razor convierte el archivo en una acción de MVC, lo que significa que puede administrar las solicitudes. @page
debe ser la primera directiva de Razor de una página. @page
y @model
son ejemplos de la transición a un marcado específico de Razor. Vea Sintaxis de Razor para obtener más información.
La directiva @model
@page
@model RazorPagesMovie.Pages.Movies.IndexModel
La directiva @model
especifica el tipo del modelo que se pasa a una instancia de Razor Pages. En el ejemplo anterior, la línea @model
permite que la clase derivada de PageModel
esté disponible en la instancia de Razor Pages. El modelo se usa en los asistentes de HTML @Html.DisplayNameFor
y @Html.DisplayFor
de la página.
Examine la expresión lambda usada en el siguiente asistente de HTML:
@Html.DisplayNameFor(model => model.Movie[0].Title)
El asistente de HTML DisplayNameFor inspecciona la propiedad Title
a la que se hace referencia en la expresión lambda para determinar el nombre para mostrar. La expresión lambda se inspecciona, no se evalúa. Esto significa que no hay ninguna infracción de acceso si model
, model.Movie
o model.Movie[0]
son null
o están vacíos. Al evaluar la expresión lambda (por ejemplo, con @Html.DisplayFor(modelItem => item.Title)
), se evalúan los valores de propiedad del modelo.
Página de diseño
Selecciona los vínculos de menú RazorPagesMovie, Home y Privacy. Cada página muestra el mismo diseño de menú. El diseño de menú se implementa en el archivo Pages/Shared/_Layout.cshtml
.
Abra y examine el archivo Pages/Shared/_Layout.cshtml
.
Las plantillas de diseño permiten que el diseño del contenedor HTML:
- Esté especificado en un solo lugr.
- Se aplique en varias páginas del sitio.
Busque la línea @RenderBody()
. RenderBody
es un marcador de posición donde se muestran todas las vistas específicas de página, encapsuladas en la página de diseño. Por ejemplo, seleccione el vínculo Privacy y la vista Pages/Privacy.cshtml
se representa dentro del método RenderBody
.
Propiedades ViewData y Layout
Tenga en cuenta el siguiente marcado del archivo Pages/Movies/Index.cshtml
:
@page
@model RazorPagesMovie.Pages.Movies.IndexModel
@{
ViewData["Title"] = "Index";
}
El marcado resaltado anterior es un ejemplo de Razor con una transición a C#. Los caracteres {
y }
delimitan un bloque de código de C#.
La clase base PageModel
contiene una propiedad de diccionario ViewData
que se puede usar para pasar datos a una vista. Los objetos se agregan al diccionario ViewData
con un patrón clave-valor. En el ejemplo anterior, la propiedad Title
se agrega al diccionario ViewData
.
La propiedad Title
se usa en el archivo Pages/Shared/_Layout.cshtml
. En el siguiente marcado se muestran las primeras líneas del archivo _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" />
La línea @*Markup removed for brevity.*@
es un comentario de Razor. A diferencia de los comentarios HTML <!-- -->
, los comentarios de Razor no se envían al cliente. Consulte Documentación web de MDN: introducción a HTML para obtener más información.
Actualizar el diseño
Cambie el elemento
<title>
del archivoPages/Shared/_Layout.cshtml
para mostrar Movie en lugar 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>
Busque el siguiente elemento delimitador en el archivo
Pages/Shared/_Layout.cshtml
.<a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
Reemplace el elemento anterior por el marcado siguiente:
<a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
El elemento delimitador anterior es un asistente de etiquetas. En este caso, se trata de el asistente de etiquetas Anchor. El atributo y valor del asistente de etiquetas
asp-page="/Movies/Index"
crea un vínculo a/Movies/Index
en Razor Pages. El valor de atributoasp-area
está vacío, por lo que no se usa el área del vínculo. Consulte Áreas para obtener más información.Guarde los cambios y pruebe la aplicación seleccionando el vínculo RpMovie (Película de RP). Si tiene cualquier problema, consulte el archivo _Layout.cshtml en GitHub.
Pruebe los vínculos Home, RpMovie, Create, Edit y Delete. Cada página establece el título, que puede ver en la pestaña del explorador. Al marcar una página, se usa el título para el marcador.
Nota
Es posible que no pueda escribir comas decimales en el campo Price
. Para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos, debe seguir unos pasos para globalizar la aplicación. Consulte este problema 4076 de GitHub para instrucciones sobre cómo agregar la coma decimal.
La propiedad Layout
se establece en el archivo Pages/_ViewStart.cshtml
:
@{
Layout = "_Layout";
}
El marcado anterior establece el archivo de diseño de todos los archivos de Razor de la carpeta Pages en Pages/Shared/_Layout.cshtml
. Vea Layout (Diseño) para más información.
Modelo de página Crear
Examine el modelo de 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");
}
}
}
El método OnGet
inicializa cualquier estado necesario para la página. La página Crear no tiene ningún estado que inicializar, de modo que se devuelve Page
. Más adelante en el tutorial se muestra un ejemplo del estado de inicialización de OnGet
. El método Page
crea un objeto PageResult
que representa la página Create.cshtml
.
La propiedad Movie
usa el atributo [BindProperty] para participar en el enlace de modelos. Cuando el formulario de creación publica los valores del formulario, el tiempo de ejecución de ASP.NET Core enlaza los valores publicados con el modelo Movie
.
El método OnPostAsync
se ejecuta cuando la página publica los datos del formulario:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Movie.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Si hay algún error de modelo, se vuelve a mostrar el formulario, junto con los datos del formulario publicados. La mayoría de los errores de modelo se pueden capturar en el cliente antes de que se publique el formulario. Un ejemplo de un error de modelo consiste en publicar un valor para el campo de fecha que no se puede convertir en una fecha. Más adelante en el tutorial, hablaremos de la validación del lado cliente y de la validación de modelos.
Si no hay ningún error de modelo:
- Los datos se guardan.
- El explorador se redirige a la página Index.
La página de creación de instancias de Razor Pages
Examine el archivo de instancia de Razor Pages Pages/Movies/Create.cshtml
:
@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");}
}
Visual Studio muestra las etiquetas siguientes con una fuente negrita diferenciada que se aplica a los asistentes de etiquetas:
<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>
El elemento <form method="post">
es un asistente de etiquetas de formulario. El asistente de etiquetas de formulario incluye automáticamente un token antifalsificación.
El motor de scaffolding crea un marcado de Razor para cada campo del modelo (excepto el identificador) similar al siguiente:
<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>
Los asistentes de etiquetas de validación (<div asp-validation-summary
y <span asp-validation-for
) muestran errores de validación. La validación se trata con más detalle en un punto posterior de esta serie.
El asistente de etiquetas (<label asp-for="Movie.Title" class="control-label"></label>
) genera el título de la etiqueta y el atributo [for]
para la propiedad Title
.
El asistente de etiquetas de entrada (<input asp-for="Movie.Title" class="form-control">
) usa los atributos DataAnnotations y genera los atributos HTML necesarios para la validación de jQuery en el lado del cliente.
Para obtener más información sobre los asistentes de etiquetas como <form method="post">
, consulte Asistentes de etiquetas en ASP.NET Core.
Pasos siguientes
Páginas de creación, eliminación, detalles y edición
Examine el modelo de Pages/Movies/Index.cshtml.cs
Pages:
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();
}
}
}
}
Las instancias de Razor Pages derivan de PageModel. Por convención, la clase derivada de PageModel
se denomina PageNameModel
. Por ejemplo, la página Index se denomina IndexModel
.
El constructor aplica la inserción de dependencias para agregar RazorPagesMovieContext
a la página:
public class IndexModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
Vea Código asincrónico para obtener más información sobre programación asincrónica con Entity Framework.
Cuando se efectúa una solicitud de la página, el método OnGetAsync
devuelve una lista de películas a la instancia de Razor Pages. En la instancia de Razor Pages, se llama a OnGetAsync
o OnGet
para inicializar el estado de la página. En este caso, OnGetAsync
obtiene una lista de películas y las muestra.
Cuando OnGet
devuelve void
o OnGetAsync
devuelve Task
, no se utiliza ninguna instrucción de devolución. Por ejemplo, examine la página Privacy:
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()
{
}
}
}
Cuando el tipo de valor devuelto es IActionResult o Task<IActionResult>
, se debe proporcionar una instrucción return. Por ejemplo, el método OnPostAsync
de Pages/Movies/Create.cshtml.cs
:
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 la instancia de Razor Pages Pages/Movies/Index.cshtml
:
@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>
Razor puede realizar la transición de HTML a C# o a un marcado específico de Razor. Cuando el símbolo @
va seguido de una palabra clave reservada de Razor, se realiza una transición a un marcado específico de Razor; en caso contrario, la transición se realiza a C#.
La directiva @page
La directiva @page
de Razor convierte el archivo en una acción de MVC, lo que significa que puede administrar las solicitudes. @page
debe ser la primera directiva de Razor de una página. @page
y @model
son ejemplos de la transición a un marcado específico de Razor. Vea Sintaxis de Razor para obtener más información.
La directiva @model
@page
@model RazorPagesMovie.Pages.Movies.IndexModel
La directiva @model
especifica el tipo del modelo que se pasa a una instancia de Razor Pages. En el ejemplo anterior, la línea @model
permite que la clase derivada de PageModel
esté disponible en la instancia de Razor Pages. El modelo se usa en los asistentes de HTML @Html.DisplayNameFor
y @Html.DisplayFor
de la página.
Examine la expresión lambda usada en el siguiente asistente de HTML:
@Html.DisplayNameFor(model => model.Movie[0].Title)
El asistente de HTML DisplayNameFor inspecciona la propiedad Title
a la que se hace referencia en la expresión lambda para determinar el nombre para mostrar. La expresión lambda se inspecciona, no se evalúa. Esto significa que no hay ninguna infracción de acceso si model
, model.Movie
o model.Movie[0]
son null
o están vacíos. Al evaluar la expresión lambda (por ejemplo, con @Html.DisplayFor(modelItem => item.Title)
), se evalúan los valores de propiedad del modelo.
Página de diseño
Selecciona los vínculos de menú RazorPagesMovie, Home y Privacy. Cada página muestra el mismo diseño de menú. El diseño de menú se implementa en el archivo Pages/Shared/_Layout.cshtml
.
Abra y examine el archivo Pages/Shared/_Layout.cshtml
.
Las plantillas de diseño permiten que el diseño del contenedor HTML:
- Esté especificado en un solo lugr.
- Se aplique en varias páginas del sitio.
Busque la línea @RenderBody()
. RenderBody
es un marcador de posición donde se muestran todas las vistas específicas de página, encapsuladas en la página de diseño. Por ejemplo, seleccione el vínculo Privacy y la vista Pages/Privacy.cshtml
se representa dentro del método RenderBody
.
Propiedades ViewData y Layout
Tenga en cuenta el siguiente marcado del archivo Pages/Movies/Index.cshtml
:
@page
@model RazorPagesMovie.Pages.Movies.IndexModel
@{
ViewData["Title"] = "Index";
}
El marcado resaltado anterior es un ejemplo de Razor con una transición a C#. Los caracteres {
y }
delimitan un bloque de código de C#.
La clase base PageModel
contiene una propiedad de diccionario ViewData
que se puede usar para pasar datos a una vista. Los objetos se agregan al diccionario ViewData
con un patrón clave-valor. En el ejemplo anterior, la propiedad Title
se agrega al diccionario ViewData
.
La propiedad Title
se usa en el archivo Pages/Shared/_Layout.cshtml
. En el siguiente marcado se muestran las primeras líneas del archivo _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" />
La línea @*Markup removed for brevity.*@
es un comentario de Razor. A diferencia de los comentarios HTML <!-- -->
, los comentarios de Razor no se envían al cliente. Consulte Documentación web de MDN: introducción a HTML para obtener más información.
Actualizar el diseño
Cambie el elemento
<title>
del archivoPages/Shared/_Layout.cshtml
para mostrar Movie en lugar 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>
Busque el siguiente elemento delimitador en el archivo
Pages/Shared/_Layout.cshtml
.<a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
Reemplace el elemento anterior por el marcado siguiente:
<a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
El elemento delimitador anterior es un asistente de etiquetas. En este caso, se trata de el asistente de etiquetas Anchor. El atributo y valor del asistente de etiquetas
asp-page="/Movies/Index"
crea un vínculo a/Movies/Index
en Razor Pages. El valor de atributoasp-area
está vacío, por lo que no se usa el área del vínculo. Consulte Áreas para obtener más información.Guarde los cambios y pruebe la aplicación seleccionando el vínculo RpMovie (Película de RP). Si tiene cualquier problema, consulte el archivo _Layout.cshtml en GitHub.
Pruebe los vínculos Home, RpMovie, Create, Edit y Delete. Cada página establece el título, que puede ver en la pestaña del explorador. Al marcar una página, se usa el título para el marcador.
Nota
Es posible que no pueda escribir comas decimales en el campo Price
. Para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos, debe seguir unos pasos para globalizar la aplicación. Consulte este problema 4076 de GitHub para instrucciones sobre cómo agregar la coma decimal.
La propiedad Layout
se establece en el archivo Pages/_ViewStart.cshtml
:
@{
Layout = "_Layout";
}
El marcado anterior establece el archivo de diseño de todos los archivos de Razor de la carpeta Pages en Pages/Shared/_Layout.cshtml
. Vea Layout (Diseño) para más información.
Modelo de página Crear
Examine el modelo de 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");
}
}
}
El método OnGet
inicializa cualquier estado necesario para la página. La página Crear no tiene ningún estado que inicializar, de modo que se devuelve Page
. Más adelante en el tutorial se muestra un ejemplo del estado de inicialización de OnGet
. El método Page
crea un objeto PageResult
que representa la página Create.cshtml
.
La propiedad Movie
usa el atributo [BindProperty] para participar en el enlace de modelos. Cuando el formulario de creación publica los valores del formulario, el tiempo de ejecución de ASP.NET Core enlaza los valores publicados con el modelo Movie
.
El método OnPostAsync
se ejecuta cuando la página publica los datos del formulario:
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");
}
Si hay algún error de modelo, se vuelve a mostrar el formulario, junto con los datos del formulario publicados. La mayoría de los errores de modelo se pueden capturar en el cliente antes de que se publique el formulario. Un ejemplo de un error de modelo consiste en publicar un valor para el campo de fecha que no se puede convertir en una fecha. Más adelante en el tutorial, hablaremos de la validación del lado cliente y de la validación de modelos.
Si no hay ningún error de modelo:
- Los datos se guardan.
- El explorador se redirige a la página Index.
La página de creación de instancias de Razor Pages
Examine el archivo de instancia de Razor Pages Pages/Movies/Create.cshtml
:
@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");}
}
Visual Studio muestra las etiquetas siguientes con una fuente negrita diferenciada que se aplica a los asistentes de etiquetas:
<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>
El elemento <form method="post">
es un asistente de etiquetas de formulario. El asistente de etiquetas de formulario incluye automáticamente un token antifalsificación.
El motor de scaffolding crea un marcado de Razor para cada campo del modelo (excepto el identificador) similar al siguiente:
<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>
Los asistentes de etiquetas de validación (<div asp-validation-summary
y <span asp-validation-for
) muestran errores de validación. La validación se trata con más detalle en un punto posterior de esta serie.
El asistente de etiquetas (<label asp-for="Movie.Title" class="control-label"></label>
) genera el título de la etiqueta y el atributo [for]
para la propiedad Title
.
El asistente de etiquetas de entrada (<input asp-for="Movie.Title" class="form-control">
) usa los atributos DataAnnotations y genera los atributos HTML necesarios para la validación de jQuery en el lado del cliente.
Para obtener más información sobre los asistentes de etiquetas como <form method="post">
, consulte Asistentes de etiquetas en ASP.NET Core.
Pasos siguientes
Páginas de creación, eliminación, detalles y edición
Examine el modelo de Pages/Movies/Index.cshtml.cs
Pages:
// 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();
}
}
}
Las instancias de Razor Pages derivan de PageModel
. Por convención, la clase derivada de PageModel
se denomina <PageName>Model
. El constructor aplica la inserción de dependencias para agregar RazorPagesMovieContext
a la página:
public class IndexModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
Vea Código asincrónico para obtener más información sobre programación asincrónica con Entity Framework.
Cuando se efectúa una solicitud de la página, el método OnGetAsync
devuelve una lista de películas a la instancia de Razor Pages. En la instancia de Razor Pages, se llama a OnGetAsync
o OnGet
para inicializar el estado de la página. En este caso, OnGetAsync
obtiene una lista de películas y las muestra.
Cuando OnGet
devuelve void
o OnGetAsync
devuelve Task
, no se utiliza ninguna instrucción de devolución. Por ejemplo, la página Privacy:
public class PrivacyModel : PageModel
{
private readonly ILogger<PrivacyModel> _logger;
public PrivacyModel(ILogger<PrivacyModel> logger)
{
_logger = logger;
}
public void OnGet()
{
}
}
Cuando el tipo de valor devuelto es IActionResult
o Task<IActionResult>
, se debe proporcionar una instrucción return. Por ejemplo, el método OnPostAsync
de Pages/Movies/Create.cshtml.cs
:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Movie.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
Examine la instancia de Razor Pages Pages/Movies/Index.cshtml
:
@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>
Razor puede realizar la transición de HTML a C# o a un marcado específico de Razor. Cuando el símbolo @
va seguido de una palabra clave reservada de Razor, se realiza una transición a un marcado específico de Razor; en caso contrario, la transición se realiza a C#.
La directiva @page
La directiva @page
de Razor convierte el archivo en una acción de MVC, lo que significa que puede administrar las solicitudes. @page
debe ser la primera directiva de Razor de una página. @page
y @model
son ejemplos de la transición a un marcado específico de Razor. Vea Sintaxis de Razor para obtener más información.
La directiva @model
@page
@model RazorPagesMovie.Pages.Movies.IndexModel
La directiva @model
especifica el tipo del modelo que se pasa a una instancia de Razor Pages. En el ejemplo anterior, la línea @model
permite que la clase derivada de PageModel
esté disponible en la instancia de Razor Pages. El modelo se usa en los asistentes de HTML @Html.DisplayNameFor
y @Html.DisplayFor
de la página.
Examine la expresión lambda usada en el siguiente asistente de HTML:
@Html.DisplayNameFor(model => model.Movie[0].Title)
El asistente de HTML DisplayNameFor inspecciona la propiedad Title
a la que se hace referencia en la expresión lambda para determinar el nombre para mostrar. La expresión lambda se inspecciona, no se evalúa. Esto significa que no hay ninguna infracción de acceso si model
, model.Movie
o model.Movie[0]
son null
o están vacíos. Al evaluar la expresión lambda (por ejemplo, con @Html.DisplayFor(modelItem => item.Title)
), se evalúan los valores de propiedad del modelo.
Página de diseño
Selecciona los vínculos de menú RazorPagesMovie, Home y Privacy. Cada página muestra el mismo diseño de menú. El diseño de menú se implementa en el archivo Pages/Shared/_Layout.cshtml
.
Abra y examine el archivo Pages/Shared/_Layout.cshtml
.
Las plantillas de diseño permiten que el diseño del contenedor HTML:
- Esté especificado en un solo lugr.
- Se aplique en varias páginas del sitio.
Busque la línea @RenderBody()
. RenderBody
es un marcador de posición donde se muestran todas las vistas específicas de página, encapsuladas en la página de diseño. Por ejemplo, seleccione el vínculo Privacy y la vista Pages/Privacy.cshtml
se representa dentro del método RenderBody
.
Propiedades ViewData y Layout
Tenga en cuenta el siguiente marcado del archivo Pages/Movies/Index.cshtml
:
@page
@model RazorPagesMovie.Pages.Movies.IndexModel
@{
ViewData["Title"] = "Index";
}
El marcado resaltado anterior es un ejemplo de Razor con una transición a C#. Los caracteres {
y }
delimitan un bloque de código de C#.
La clase base PageModel
contiene una propiedad de diccionario ViewData
que se puede usar para pasar datos a una vista. Los objetos se agregan al diccionario ViewData
con un patrón clave-valor. En el ejemplo anterior, la propiedad Title
se agrega al diccionario ViewData
.
La propiedad Title
se usa en el archivo Pages/Shared/_Layout.cshtml
. En el siguiente marcado se muestran las primeras líneas del archivo _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.*@
La línea @*Markup removed for brevity.*@
es un comentario de Razor. A diferencia de los comentarios HTML <!-- -->
, los comentarios de Razor no se envían al cliente. Consulte Documentación web de MDN: introducción a HTML para obtener más información.
Actualizar el diseño
Cambie el elemento
<title>
del archivoPages/Shared/_Layout.cshtml
para mostrar Movie en lugar 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>
Busque el siguiente elemento delimitador en el archivo
Pages/Shared/_Layout.cshtml
.<a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
Reemplace el elemento anterior por el marcado siguiente:
<a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
El elemento delimitador anterior es un asistente de etiquetas. En este caso, se trata de el asistente de etiquetas Anchor. El atributo y valor del asistente de etiquetas
asp-page="/Movies/Index"
crea un vínculo a/Movies/Index
en Razor Pages. El valor de atributoasp-area
está vacío, por lo que no se usa el área del vínculo. Consulte Áreas para obtener más información.Guarde los cambios y pruebe la aplicación seleccionando el vínculo RpMovie (Película de RP). Si tiene cualquier problema, consulte el archivo _Layout.cshtml en GitHub.
Pruebe los vínculos Home, RpMovie, Create, Edit y Delete. Cada página establece el título, que puede ver en la pestaña del explorador. Al marcar una página, se usa el título para el marcador.
Nota
Es posible que no pueda escribir comas decimales en el campo Price
. Para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos, debe seguir unos pasos para globalizar la aplicación. Consulte este problema 4076 de GitHub para instrucciones sobre cómo agregar la coma decimal.
La propiedad Layout
se establece en el archivo Pages/_ViewStart.cshtml
:
@{
Layout = "_Layout";
}
El marcado anterior establece el archivo de diseño de todos los archivos de Razor de la carpeta Pages en Pages/Shared/_Layout.cshtml
. Vea Layout (Diseño) para más información.
Modelo de página Crear
Examine el modelo de 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");
}
}
}
El método OnGet
inicializa cualquier estado necesario para la página. La página Crear no tiene ningún estado que inicializar, de modo que se devuelve Page
. Más adelante en el tutorial se muestra un ejemplo del estado de inicialización de OnGet
. El método Page
crea un objeto PageResult
que representa la página Create.cshtml
.
La propiedad Movie
usa el atributo [BindProperty] para participar en el enlace de modelos. Cuando el formulario de creación publica los valores del formulario, el tiempo de ejecución de ASP.NET Core enlaza los valores publicados con el modelo Movie
.
El método OnPostAsync
se ejecuta cuando la página publica los datos del formulario:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Movie.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Si hay algún error de modelo, se vuelve a mostrar el formulario, junto con los datos del formulario publicados. La mayoría de los errores de modelo se pueden capturar en el cliente antes de que se publique el formulario. Un ejemplo de un error de modelo consiste en publicar un valor para el campo de fecha que no se puede convertir en una fecha. Más adelante en el tutorial, hablaremos de la validación del lado cliente y de la validación de modelos.
Si no hay ningún error de modelo:
- Los datos se guardan.
- El explorador se redirige a la página Index.
La página de creación de instancias de Razor Pages
Examine el archivo de instancia de Razor Pages Pages/Movies/Create.cshtml
:
@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");}
}
Visual Studio muestra las etiquetas siguientes con una fuente negrita diferenciada que se aplica a los asistentes de etiquetas:
<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>
El elemento <form method="post">
es un asistente de etiquetas de formulario. El asistente de etiquetas de formulario incluye automáticamente un token antifalsificación.
El motor de scaffolding crea un marcado de Razor para cada campo del modelo (excepto el identificador) similar al siguiente:
<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>
Los asistentes de etiquetas de validación (<div asp-validation-summary
y <span asp-validation-for
) muestran errores de validación. La validación se trata con más detalle en un punto posterior de esta serie.
El asistente de etiquetas (<label asp-for="Movie.Title" class="control-label"></label>
) genera el título de la etiqueta y el atributo [for]
para la propiedad Title
.
El asistente de etiquetas de entrada (<input asp-for="Movie.Title" class="form-control">
) usa los atributos DataAnnotations y genera los atributos HTML necesarios para la validación de jQuery en el lado del cliente.
Para obtener más información sobre los asistentes de etiquetas como <form method="post">
, consulte Asistentes de etiquetas en ASP.NET Core.