Componentes de Razor de 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 ASP.NET Core 8.0 de este artículo.

En este artículo se explica cómo crear y usar componentes de Razor en aplicaciones de Blazor, incluidas instrucciones sobre la sintaxis Razor, la nomenclatura de componentes, los espacios de nombres y los parámetros de componente.

Componentes Razor

Las aplicaciones Blazor se crean usando componentes de Razor, conocidos de forma informal como componentes de Blazor o solo componentes. Un componente es una parte independiente de la interfaz de usuario (UI) con lógica de procesamiento para habilitar el comportamiento dinámico. Los componentes se pueden anidar, reusar y compartir entre proyectos, además de usarlos en aplicaciones de páginas Razor y MVC.

Los componentes se representan en una representación en memoria del elemento Document Object Model (DOM) del explorador denominado árbol de representación, que se usa para actualizar la interfaz de usuario de manera eficaz y flexible.

Clases de componentes

Los componentes se implementan en archivos de componentes de Razor con una extensión de archivo .razor mediante una combinación de C# y marcado HTML.

De forma predeterminada, ComponentBase es la clase base para los componentes descritos por los archivos de componente de Razor. ComponentBase implementa la abstracción más baja de los componentes, la interfaz IComponent. ComponentBase define las propiedades y los métodos de componente para la funcionalidad básica, por ejemplo, para procesar un conjunto de eventos de ciclo de vida de componentes integrados.

ComponentBase en dotnet/aspnetcore origen de referencia: el origen de referencia contiene comentarios adicionales sobre los eventos de ciclo de vida integrados. Sin embargo, tenga en cuenta que las implementaciones internas de las características de componente están sujetas a cambios en cualquier momento sin previo aviso.

Nota

Los vínculos de la documentación al origen de referencia de .NET cargan normalmente la rama predeterminada del repositorio, que representa el desarrollo actual para la próxima versión de .NET. Para seleccionar una etiqueta de una versión específica, use la lista desplegable Cambiar ramas o etiquetas. Para obtener más información, vea Procedimientos para seleccionar una etiqueta de versión de código fuente de ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Normalmente, los desarrolladores crean Razor componentes a partir de archivos de componentes Razor (.razor) o basan sus componentes en ComponentBase, pero también se pueden compilar mediante la implementación de IComponent. Los componentes creados por el desarrollador que implementan IComponent pueden tomar un control de bajo nivel sobre la representación a costa de tener que desencadenar manualmente la representación con eventos y métodos de ciclo de vida que el desarrollador debe crear y mantener.

Sintaxis de Razor

Los componentes usan la sintaxis de Razor. Los componentes, las directivas y los atributos de directiva usan ampliamente dos características de Razor. Estas son palabras clave reservadas con el prefijo @ que aparecen en el marcado de Razor:

  • Directivas: cambian el modo en que se analiza o funciona un marcado de componente. Por ejemplo, la directiva @page especifica un componente enrutable con una plantilla de ruta, y se puede acceder directamente a esta mediante la solicitud de un usuario en el explorador en una dirección URL específica.

    Por convención, las directivas de un componente en la parte superior de una definición de componente (archivo .razor) se colocan en un orden coherente. En el caso de las directivas repetidas, las directivas se colocan alfabéticamente por espacio de nombres o tipo, excepto las directivas @using, que tienen una ordenación especial de segundo nivel.

    La documentación y las aplicaciones de ejemplo de Blazor adoptan el siguiente orden. Los componentes que proporciona una plantilla de proyecto de Blazor pueden diferir de los del siguiente orden y usar otro formato. Por ejemplo, los componentes Blazor del marco Identity incluyen líneas en blanco entre los bloques de directivas @using y los de directivas @inject. Puede usar un formato y esquema de ordenación personalizados en sus propias aplicaciones.

    Documentación y orden de directiva de Razor de la aplicación de ejemplo:

    • @page
    • @rendermode (.NET 8 o posterior)
    • @using
      • Espacios de nombres de System (orden alfabético)
      • Espacios de nombres de Microsoft (orden alfabético)
      • Espacios de nombres de API de terceros (orden alfabético)
      • Espacios de nombres de aplicación (orden alfabético)
    • Otras directivas (orden alfabético)

    No aparece ninguna línea en blanco entre las directivas. Aparece una línea en blanco entre las directivas y la primera línea del marcado de Razor.

    Ejemplo:

    @page "/doctor-who-episodes/{season:int}"
    @rendermode InteractiveWebAssembly
    @using System.Globalization
    @using System.Text.Json
    @using Microsoft.AspNetCore.Localization
    @using Mandrill
    @using BlazorSample.Components.Layout
    @attribute [Authorize]
    @implements IAsyncDisposable
    @inject IJSRuntime JS
    @inject ILogger<DoctorWhoEpisodes> Logger
    
    <PageTitle>Doctor Who Episode List</PageTitle>
    
    ...
    
  • Atributos de directiva: cambian el modo en que se analiza o funciona un elemento de componente.

    Ejemplo:

    <input @bind="episodeId" />
    

Las directivas y los atributos de directiva que se usan en los componentes se explican más adelante en este artículo y en otros artículos del conjunto de documentación de Blazor. Para obtener información general sobre la sintaxis Razor, consulte Referencia sobre la sintaxis Razor para ASP.NET Core.

Nombre de componente, nombre de clase y espacio de nombres

El nombre de un componente debe empezar por mayúsculas:

Se admite:ProductDetail.razor

No admitido:productDetail.razor

Entre las convenciones de nomenclatura comunes de Blazor que se usan en toda la documentación de Blazor se incluyen:

  • Las rutas de acceso de archivo y los nombres de archivo usan Pascal Case† y aparecen antes de mostrar ejemplos de código. Si hay una ruta de acceso presente, indica la ubicación típica de la carpeta. Por ejemplo, Components/Pages/ProductDetail.razor indica que el componente ProductDetail tiene el nombre de archivo ProductDetail.razor y reside en la carpeta Pages de la carpeta Components de la aplicación.
  • Las rutas de archivo de componente para los componentes enrutables hacen coincidir sus direcciones URL en kebab case‡ con guiones que aparecen entre palabras en la plantilla de ruta de un componente. Por ejemplo, se solicita un componente ProductDetail con una plantilla de ruta de /product-detail (@page "/product-detail") en un explorador en la dirección URL /product-detail relativa.

†Pascal Case (letra mayúscula) es una convención de nomenclatura sin espacios y signos de puntuación y con la primera letra de cada palabra en mayúsculas, incluida la primera palabra.
‡Kebab case es una convención de nomenclatura sin espacios y signos de puntuación que usa letras minúsculas y guiones entre palabras.

Los componentes son clases de C# normales, y se pueden colocar en cualquier parte dentro de un proyecto. Los componentes que generan páginas web suelen residir en la carpeta Components/Pages. Los componentes que no son de página se colocan con frecuencia en la carpeta Components o en una carpeta personalizada agregada al proyecto.

Por lo general, el espacio de nombres de un componente se deriva del espacio de nombres raíz de la aplicación y de la ubicación del componente (carpeta) dentro de la aplicación. Así, si el espacio de nombres raíz de la aplicación es BlazorSample y el componente Counter reside en la carpeta Components/Pages:

  • El espacio de nombres del componente Counter es BlazorSample.Components.Pages.
  • El nombre de tipo completo del componente es BlazorSample.Components.Pages.Counter.

En el caso de las carpetas personalizadas que contienen componentes, agregue una directiva @using al componente primario o al archivo _Imports.razor de la aplicación. En el ejemplo siguiente se ponen a disposición los componentes de la carpeta AdminComponents:

@using BlazorSample.AdminComponents

Nota

Las directivas @using del archivo _Imports.razor solo se aplican a los archivos Razor (.razor), por lo que no se aplican a los archivos C# (.cs).

Se admiten instrucciones using con alias. En el ejemplo siguiente, la clase pública WeatherForecast del componente GridRendering está disponible como WeatherForecast en un componente en otra parte de la aplicación:

@using WeatherForecast = Components.Pages.GridRendering.WeatherForecast

También se puede hacer referencia a los componentes mediante sus nombres completos, lo cual no requiere el uso de la directiva @using. En el ejemplo siguiente se hace referencia directamente al componente ProductDetail de la carpeta AdminComponents/Pages de la aplicación:

<BlazorSample.AdminComponents.Pages.ProductDetail />

El espacio de nombres de un componente creado con Razor se basa en lo siguiente (por orden de prioridad):

  • Directiva @namespace en el marcado del archivo de Razor (por ejemplo, @namespace BlazorSample.CustomNamespace).
  • Elemento RootNamespace del proyecto en el archivo de proyecto (por ejemplo, <RootNamespace>BlazorSample</RootNamespace>).
  • Espacio de nombres del proyecto y la ruta de acceso de la raíz del proyecto al componente. Por ejemplo, el marco de trabajo resuelve {PROJECT NAMESPACE}/Components/Pages/Home.razor con un espacio de nombres de proyecto de BlazorSample en el espacio de nombres BlazorSample.Components.Pages del componente Home. {PROJECT NAMESPACE} es el espacio de nombres del proyecto. Los componentes de C# siguen las reglas de los enlaces de nombres. En el caso del componente Home de este ejemplo, los componentes en el ámbito serían todos los componentes:
    • Ejecute Components/Pages en la misma carpeta.
    • Los componentes en la raíz del proyecto que no especifiquen explícitamente un espacio de nombres diferente.

No se admite lo siguiente:

  • La calificación global::.
  • Nombres parcialmente completos. Por ejemplo, no puede agregar @using BlazorSample.Components a un componente y, después, hacer referencia al componente NavMenu en la carpeta Components/Layout de la aplicación (Components/Layout/NavMenu.razor) con <Layout.NavMenu></Layout.NavMenu>.

El nombre de un componente debe empezar por mayúsculas:

Se admite:ProductDetail.razor

No admitido:productDetail.razor

Entre las convenciones de nomenclatura comunes de Blazor que se usan en toda la documentación de Blazor se incluyen:

  • Las rutas de acceso de archivo y los nombres de archivo usan Pascal Case† y aparecen antes de mostrar ejemplos de código. Si hay una ruta de acceso presente, indica la ubicación típica de la carpeta. Por ejemplo, Pages/ProductDetail.razor indica que el componente ProductDetail tiene el nombre de archivo ProductDetail.razor y reside en la carpeta Pages de la aplicación.
  • Las rutas de archivo de componente para los componentes enrutables hacen coincidir sus direcciones URL en kebab case‡ con guiones que aparecen entre palabras en la plantilla de ruta de un componente. Por ejemplo, se solicita un componente ProductDetail con una plantilla de ruta de /product-detail (@page "/product-detail") en un explorador en la dirección URL /product-detail relativa.

†Pascal Case (letra mayúscula) es una convención de nomenclatura sin espacios y signos de puntuación y con la primera letra de cada palabra en mayúsculas, incluida la primera palabra.
‡Kebab case es una convención de nomenclatura sin espacios y signos de puntuación que usa letras minúsculas y guiones entre palabras.

Los componentes son clases de C# normales, y se pueden colocar en cualquier parte dentro de un proyecto. Los componentes que generan páginas web suelen residir en la carpeta Pages. Los componentes que no son de página se colocan con frecuencia en la carpeta Shared o en una carpeta personalizada agregada al proyecto.

Por lo general, el espacio de nombres de un componente se deriva del espacio de nombres raíz de la aplicación y de la ubicación del componente (carpeta) dentro de la aplicación. Así, si el espacio de nombres raíz de la aplicación es BlazorSample y el componente Counter reside en la carpeta Pages:

  • El espacio de nombres del componente Counter es BlazorSample.Pages.
  • El nombre de tipo completo del componente es BlazorSample.Pages.Counter.

En el caso de las carpetas personalizadas que contienen componentes, agregue una directiva @using al componente primario o al archivo _Imports.razor de la aplicación. En el ejemplo siguiente se ponen a disposición los componentes de la carpeta AdminComponents:

@using BlazorSample.AdminComponents

Nota

Las directivas @using del archivo _Imports.razor solo se aplican a los archivos Razor (.razor), por lo que no se aplican a los archivos C# (.cs).

Se admiten instrucciones using con alias. En el ejemplo siguiente, la clase pública WeatherForecast del componente GridRendering está disponible como WeatherForecast en un componente en otra parte de la aplicación:

@using WeatherForecast = Pages.GridRendering.WeatherForecast

También se puede hacer referencia a los componentes mediante sus nombres completos, lo cual no requiere el uso de la directiva @using. En el ejemplo siguiente se hace referencia directamente al componente ProductDetail de la carpeta Components de la aplicación:

<BlazorSample.Components.ProductDetail />

El espacio de nombres de un componente creado con Razor se basa en lo siguiente (por orden de prioridad):

  • Directiva @namespace en el marcado del archivo de Razor (por ejemplo, @namespace BlazorSample.CustomNamespace).
  • Elemento RootNamespace del proyecto en el archivo de proyecto (por ejemplo, <RootNamespace>BlazorSample</RootNamespace>).
  • Espacio de nombres del proyecto y la ruta de acceso de la raíz del proyecto al componente. Por ejemplo, el marco de trabajo resuelve {PROJECT NAMESPACE}/Pages/Index.razor con un espacio de nombres de proyecto de BlazorSample en el espacio de nombres BlazorSample.Pages del componente Index. {PROJECT NAMESPACE} es el espacio de nombres del proyecto. Los componentes de C# siguen las reglas de los enlaces de nombres. En el caso del componente Index de este ejemplo, los componentes en el ámbito serían todos los componentes:
    • Ejecute Pages en la misma carpeta.
    • Los componentes en la raíz del proyecto que no especifiquen explícitamente un espacio de nombres diferente.

No se admite lo siguiente:

  • La calificación global::.
  • Nombres parcialmente completos. Por ejemplo, no puede agregar @using BlazorSample a un componente y, después, hacer referencia al componente NavMenu en la carpeta Shared de la aplicación (Shared/NavMenu.razor) con <Shared.NavMenu></Shared.NavMenu>.

Compatibilidad parcial de clases

Los componentes se generan como clases parciales de C# y se crean mediante cualquiera de los siguientes métodos:

  • Un solo archivo contiene código de C# definido en uno o varios bloques @code, marcado HTML y marcado de Razor. Las plantillas de proyecto de Blazor definen sus componentes con este método de archivo único.
  • El marcado HTML y de Razor se colocan en un archivo de Razor (.razor). El código de C# se coloca en un archivo de código subyacente definido como una clase parcial (.cs).

Nota

Una hoja de estilos de componente que define estilos específicos del componente es un archivo independiente (.css). El aislamiento de CSS de Blazor se describe más adelante en Aislamiento de CSS de Blazor de ASP.NET Core.

En el siguiente ejemplo se muestra el componente Counter predeterminado con un bloque @code en una aplicación generada a partir de una plantilla de proyecto de Blazor. El marcado y el código de C# se encuentran en el mismo archivo. Este es el método más común que se aplica en la creación de componentes.

Counter.razor:

@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

El componente Counter siguiente divide el marcado HTML de presentación y de Razor del código de C# mediante un archivo de código subyacente con una clase parcial. La división del marcado del código de C# es favorecida por algunas organizaciones y desarrolladores para organizar su código de componente para adaptarse a cómo prefieren trabajar. Por ejemplo, el experto en la interfaz de usuario de la organización puede trabajar en la capa de presentación independientemente de otro desarrollador que trabaje en la lógica de C# del componente. El enfoque también es útil cuando se trabaja con el código generado automáticamente o los generadores de código fuente. Para obtener más información, consulte Clases y métodos parciales (Guía de programación de C#).

CounterPartialClass.razor:

@page "/counter-partial-class"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@page "/counter-partial-class"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@page "/counter-partial-class"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@page "/counter-partial-class"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@page "/counter-partial-class"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

CounterPartialClass.razor.cs:

namespace BlazorSample.Components.Pages;

public partial class CounterPartialClass
{
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
namespace BlazorSample.Pages;

public partial class CounterPartialClass
{
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
namespace BlazorSample.Pages
{
    public partial class CounterPartialClass
    {
        private int currentCount = 0;

        private void IncrementCount()
        {
            currentCount++;
        }
    }
}

Las directivas @using del archivo _Imports.razor solo se aplican a los archivos Razor (.razor), por lo que no se aplican a los archivos C# (.cs). Agregue los espacios de nombres que sean necesarios a un archivo de clase parcial.

Espacios de nombres típicos usados por los componentes:

using System.Net.Http;
using System.Net.Http.Json;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Sections
using Microsoft.AspNetCore.Components.Web;
using static Microsoft.AspNetCore.Components.Web.RenderMode;
using Microsoft.AspNetCore.Components.Web.Virtualization;
using Microsoft.JSInterop;

Los espacios de nombres típicos también incluyen el espacio de nombres de la aplicación y el espacio de nombres correspondiente a la carpeta Components de la aplicación:

using BlazorSample;
using BlazorSample.Components;

También se pueden incluir carpetas adicionales, como la carpeta Layout:

using BlazorSample.Components.Layout;
using System.Net.Http;
using System.Net.Http.Json;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.Web.Virtualization;
using Microsoft.JSInterop;

Los espacios de nombres típicos también incluyen el espacio de nombres de la aplicación y el espacio de nombres correspondiente a la carpeta Shared de la aplicación:

using BlazorSample;
using BlazorSample.Shared;
using System.Net.Http;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;

Los espacios de nombres típicos también incluyen el espacio de nombres de la aplicación y el espacio de nombres correspondiente a la carpeta Shared de la aplicación:

using BlazorSample;
using BlazorSample.Shared;

Especificación de una clase base

La directiva @inherits se usa para especificar la clase base de un componente. A diferencia del uso de clases parciales, que solo dividen el marcado de la lógica de C#, el uso de una clase base permite heredar código de C# para su uso en un grupo de componentes que comparten las propiedades y métodos de la clase base. El uso de clases base reduce la redundancia de código en las aplicaciones y resulta útil al proporcionar código base de bibliotecas de clases a varias aplicaciones. Para obtener más información, consulte Herencia en C# y .NET.

En el ejemplo siguiente, la clase base BlazorRocksBase1 deriva de ComponentBase.

BlazorRocks1.razor:

@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<PageTitle>Blazor Rocks!</PageTitle>

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<PageTitle>Blazor Rocks!</PageTitle>

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<PageTitle>Blazor Rocks!</PageTitle>

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>

BlazorRocksBase1.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}

Enrutamiento

El enrutamiento en Blazor se consigue proporcionando una plantilla de ruta a cada componente accesible en la aplicación con una directiva @page. Cuando se compila un archivo de Razor con una directiva @page, la clase generada recibe un elemento RouteAttribute que especifica la plantilla de ruta. En tiempo de ejecución, el enrutador busca las clases de componentes con un atributo RouteAttribute y representa el componente que tenga una plantilla de ruta que coincida con la dirección URL solicitada.

El componente siguiente HelloWorld usa una plantilla de ruta de /hello-world, y se alcanza la página web representada para el componente en la dirección URL /hello-world relativa.

HelloWorld.razor:

@page "/hello-world"

<PageTitle>Hello World!</PageTitle>

<h1>Hello World!</h1>
@page "/hello-world"

<h1>Hello World!</h1>
@page "/hello-world"

<h1>Hello World!</h1>
@page "/hello-world"

<h1>Hello World!</h1>
@page "/hello-world"

<h1>Hello World!</h1>

El componente anterior se carga en el explorador en /hello-world independientemente de si agrega o no el componente a la navegación de la interfaz de usuario de la aplicación. De manera opcional, se pueden agregar componentes al componente NavMenu para que aparezca un vínculo al componente en la navegación basada en la interfaz de usuario de la aplicación.

Para el componente HelloWorld anterior, puede agregar un componente NavLink al componente NavMenu. Para obtener más información, incluidas las descripciones de los componentes NavLink y NavMenu, consulte Enrutamiento y navegación de Blazor de ASP.NET Core.

marcado

La interfaz de usuario de un componente se define mediante la sintaxis de Razor, que consta de marcado de Razor, C# y HTML. Cuando una aplicación se compila, el marcado HTML y la lógica de representación de C# se convierten en una clase de componente. El nombre de la clase generada coincide con el nombre del archivo.

Los miembros de la clase de componente se definen en uno o varios bloques @code. En los bloques @code, el estado del componente se especifica y se procesa con C#:

  • Inicializadores de propiedad y campo.
  • Valores de parámetro de argumentos pasados por componentes primarios y parámetros de ruta.
  • Métodos para el control de eventos de usuario, eventos de ciclo de vida y lógica de componentes personalizados.

Los miembros de componente se usan en la lógica de representación mediante expresiones de C# que comienzan por el símbolo @. Por ejemplo, un campo de C# se representa anteponiendo el prefijo @ al nombre del campo. En el componente Markup siguiente se evalúa y representa lo siguiente:

  • headingFontStyle para el valor de la propiedad CSS font-style del elemento de encabezado.
  • headingText para el contenido del elemento de encabezado.

Markup.razor:

@page "/markup"

<PageTitle>Markup</PageTitle>

<h1>Markup Example</h1>

<h2 style="font-style:@headingFontStyle">@headingText</h2>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}
@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}
@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}
@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}
@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}

Nota

En los ejemplos de la documentación de Blazor se especifica el modificador de acceso private para los miembros privados. Los miembros privados tienen como ámbito la clase de un componente. Sin embargo, C# asume el modificador de acceso private cuando no hay ningún modificador de acceso presente, por lo que marcar explícitamente los miembros como "private" en su propio código es opcional. Para obtener más información sobre los modificadores de acceso, vea Modificadores de acceso (Guía de programación de C#).

El marco de Blazor procesa un componente internamente como un árbol de representación, que es la combinación del DOM y Cascading Style Sheet Object Model (CSSOM) de un componente. Una vez que el componente se ha representado inicialmente, se vuelve a generar su árbol de representación en respuesta a eventos. Blazor compara el nuevo árbol de representación con el anterior y aplica las modificaciones necesarias al DOM del explorador para su presentación. Para obtener más información, consulte Representación de componentes de Razor de ASP.NET Core.

En la sintaxis de Razor, las estructuras de control, directivas y atributos de directiva de C# se escriben en minúscula (por ejemplo, @if, @code y @bind). Los nombres de propiedad se escriben en mayúscula (por ejemplo, @Body para LayoutComponentBase.Body).

Los métodos asincrónicos (async) no admiten la devolución de void

El marco Blazor no realiza un seguimiento de seguimiento de los métodos asincrónicos que devuelven void (async). Como resultado, no se detectan excepciones si se devuelve void. Devuelva siempre una Task de los métodos asincrónicos.

Componentes anidados

Para que los componentes puedan incluir otros componentes, hay que declararlos usando la sintaxis HTML. El marcado para utilizar un componente se parece a una etiqueta HTML en la que el nombre de la etiqueta es el tipo de componente.

Tenga en cuenta el componente Heading siguiente, que pueden usar otros componentes para mostrar un encabezado.

Heading.razor:

<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}

El marcado siguiente del componente HeadingExample representa el componente Heading anterior en la ubicación donde aparece la etiqueta <Heading />.

HeadingExample.razor:

@page "/heading-example"

<PageTitle>Heading</PageTitle>

<h1>Heading Example</h1>

<Heading />
@page "/heading-example"

<Heading />
@page "/heading-example"

<Heading />
@page "/heading-example"

<Heading />
@page "/heading-example"

<Heading />

Si un componente contiene un elemento HTML cuyo nombre empieza por mayúsculas y no coincide con un nombre de componente con el mismo espacio de nombres, se emite una advertencia que indica que el elemento tiene un nombre inesperado. Si se agrega una directiva @using relativa al espacio de nombres del componente, esto permite que el componente esté disponible, lo que resuelve la advertencia. Para obtener más información, consulte la sección Nombre del componente, nombre de clase y espacio de nombres.

El ejemplo de componente Heading que se muestra en esta sección no tiene una directiva @page, por lo que el componente Heading no es accesible directamente para un usuario por medio de una solicitud directa en el explorador. Sin embargo, cualquier componente con una directiva @page se puede anidar en otro componente. Si el componente Heading era accesible directamente mediante la inclusión de @page "/heading" en la parte superior de su archivo de Razor, el componente se representaría para las solicitudes del explorador en /heading y /heading-example.

Parámetros del componente

Los parámetros del componente pasan los datos a componentes y se definen por medio de propiedades de C# públicas en la clase del componente con el atributo [Parameter]. En el ejemplo siguiente, un tipo de referencia integrado (System.String) y un tipo de referencia definido por el usuario (PanelBody) se pasan como parámetros del componente.

PanelBody.cs:

namespace BlazorSample;

public class PanelBody
{
    public string? Text { get; set; }
    public string? Style { get; set; }
}
public class PanelBody
{
    public string? Text { get; set; }
    public string? Style { get; set; }
}
public class PanelBody
{
    public string? Text { get; set; }
    public string? Style { get; set; }
}
public class PanelBody
{
    public string Text { get; set; }
    public string Style { get; set; }
}
public class PanelBody
{
    public string Text { get; set; }
    public string Style { get; set; }
}

ParameterChild.razor:

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new PanelBody()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}

Advertencia

Se admite el suministro de valores iniciales para los parámetros del componente, pero no cree un componente que escriba en sus propios parámetros después de que el componente se represente por primera vez. Para más información, consulte Evitar sobrescribir parámetros en ASP.NET Core Blazor.

Los parámetros del componente Title y Body del componente ParameterChild se establecen mediante argumentos en la etiqueta HTML que representa la instancia del componente. El componente ParameterParent siguiente representa dos componentes ParameterChild:

  • El primer componente ParameterChild se representa sin proporcionar argumentos de parámetro.
  • El segundo componente ParameterChild recibe valores para Title y Body del componente ParameterParent, que usa una expresión de C# explícita para establecer los valores de las propiedades de PanelBody.

Parameter1.razor:

@page "/parameter-1"

<PageTitle>Parameter 1</PageTitle>

<h1>Parameter Example 1</h1>

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

ParameterParent.razor:

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

ParameterParent.razor:

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

ParameterParent.razor:

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

ParameterParent.razor:

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

El siguiente marcado HTML representado del componente ParameterParent muestra los valores predeterminados del componente ParameterChild cuando el componente ParameterParent no proporciona valores de parámetros del componente. Cuando el componente ParameterParent proporciona valores de parámetros del componente, estos reemplazan los valores predeterminados del componente ParameterChild.

Nota

A modo de aclaración, las clases de estilo CSS representadas no se muestran en el siguiente marcado HTML representado.

<h1>Child component (without attribute values)</h1>

<div>
    <div>Set By Child</div>
    <div>Set by child.</div>
</div>

<h1>Child component (with attribute values)</h1>

<div>
    <div>Set by Parent</div>
    <div>Set by parent.</div>
</div>

Asigne un campo, propiedad o resultado de C# de un método a un parámetro del componente como un valor de atributo HTML. El valor del atributo normalmente puede ser cualquier expresión de C# que coincida con el tipo del parámetro. El valor del atributo puede conducir opcionalmente con un símbolo @ reservado Razor, pero no es necesario.

Si el parámetro de componente es de tipo cadena, el valor del atributo se trata en su lugar como literal de cadena C# de forma predeterminada. Si quiere especificar una expresión de C# en su lugar, use el prefijo @.

El componente ParameterParent2 siguiente muestra cuatro instancias del componente ParameterChild anterior y establece sus valores de parámetro Title en:

Las comillas alrededor de los valores de atributo de parámetro son opcionales en la mayoría de los casos según la especificación HTML5. Por ejemplo, se admite Value=this, en lugar de Value="this". Sin embargo, se recomienda usar comillas porque es más fácil de recordar y se adopta ampliamente en las tecnologías basadas en Web.

Ejemplos de código en toda la documentación:

  • Use siempre comillas. Ejemplo: Value="this".
  • No use el prefijo @ con valores no literales a menos que sea necesario. Ejemplo: Count="ct", donde ct es una variable de tipo de número. Count="@ct" es un enfoque estilístico válido, pero la documentación y los ejemplos no adoptan la convención.
  • Los literales siempre evitan @ fuera de las expresiones Razor. Ejemplo: IsFixed="true". Esto incluye palabras clave (por ejemplo, this) y null, pero puede optar por usarlas si lo desea. Por ejemplo, IsFixed="@true" no es habitual, pero se admite.

Parameter2.razor:

@page "/parameter-2"

<PageTitle>Parameter 2</PageTitle>

<h1>Parameter Example 2</h1>

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

ParameterParent2.razor:

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

ParameterParent2.razor:

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

ParameterParent2.razor:

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new PanelData();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

Nota

Al asignar un miembro de C# a un parámetro de componente, no agregue un prefijo al atributo HTML del parámetro con @.

Correcto (Title es un parámetro de cadena, Count es un parámetro con tipo de número):

<ParameterChild Title="@title" Count="ct" />
<ParameterChild Title="@title" Count="@ct" />

Incorrecto:

<ParameterChild @Title="@title" @Count="ct" />
<ParameterChild @Title="@title" @Count="@ct" />

A diferencia de lo que ocurre en Razor Pages (.cshtml), Blazor no puede realizar el trabajo asincrónico en una expresión de Razor mientras se representa un componente. Esto se debe a que Blazor está diseñado para representar interfaces de usuario interactivas. En una interfaz de usuario interactiva, la pantalla debe mostrar siempre algo, por lo que no tiene sentido bloquear el flujo de representación. En su lugar, el trabajo asincrónico se realiza durante uno de los eventos asincrónicos del ciclo de vida. Después de cada evento asincrónico del ciclo de vida, el componente puede representarse de nuevo. La sintaxis de Razor siguiente no se admite:

<ParameterChild Title="@await ..." />

El código del ejemplo anterior genera un error del compilador si se compila la aplicación:

El operador "await" solo se puede usar dentro de un método asincrónico. Marque este método con el modificador "Async" y cambie su tipo de valor devuelto a "Task".

Para obtener un valor para el parámetro Title en el ejemplo anterior de manera asincrónica, el componente puede usar el evento del ciclo de vida OnInitializedAsync, como se muestra en el ejemplo siguiente:

<ParameterChild Title="@title" />

@code {
    private string? title;
    
    protected override async Task OnInitializedAsync()
    {
        title = await ...;
    }
}

Para más información, consulte Ciclo de vida de los componentes de ASP.NET Core Razor.

RazorNo se admite el uso de una expresión de explícita para concatenar texto con el resultado de una expresión para la asignación a un parámetro. En el ejemplo siguiente se busca concatenar el texto "Set by " con el valor de propiedad de un objeto. Aunque esta sintaxis se admite en una página de Razor (.cshtml), no es válida para la asignación al parámetro Title del elemento secundario de un componente. La sintaxis de Razor siguiente no se admite:

<ParameterChild Title="Set by @(panelData.Title)" />

El código del ejemplo anterior genera un error del compilador si se compila la aplicación:

Los atributos del componente no admiten contenido complejo (C# y marcado combinado).

Para admitir la asignación de un valor compuesto, utilice un método, un campo o una propiedad. En el ejemplo siguiente se realiza la concatenación de "Set by " y el valor de propiedad de un objeto en el método de C# GetTitle:

Parameter3.razor:

@page "/parameter-3"

<PageTitle>Parameter 3</PageTitle>

<h1>Parameter Example 3</h1>

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

ParameterParent3.razor:

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

ParameterParent3.razor:

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

ParameterParent3.razor:

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

ParameterParent3.razor:

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new PanelData();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

Para obtener más información, consulte la referencia sobre la sintaxis de Razor para ASP.NET Core.

Advertencia

Se admite el suministro de valores iniciales para los parámetros del componente, pero no cree un componente que escriba en sus propios parámetros después de que el componente se represente por primera vez. Para más información, consulte Evitar sobrescribir parámetros en ASP.NET Core Blazor.

Los parámetros del componente se deben declarar como propiedades automáticas, lo que significa que no deben contener lógica personalizada en sus descriptores de acceso get o set. Por ejemplo, la siguiente propiedad StartData es una propiedad automática:

[Parameter]
public DateTime StartData { get; set; }

No coloque la lógica personalizada en el descriptor de acceso get o set porque los parámetros del componente están pensados exclusivamente para su uso como canal para que un componente primario envíe información a un componente secundario. Si un descriptor de acceso set de una propiedad de componente secundario contiene lógica que provoca la repetición de la representación del componente primario, se produce un bucle de representación infinito.

Para transformar un valor de parámetro recibido:

  • Deje la propiedad del parámetro como propiedad automática para representar los datos sin procesar proporcionados.
  • Cree otra propiedad o método que proporcione los datos transformados en función de la propiedad del parámetro.

Invalide OnParametersSetAsync para transformar un parámetro recibido cada vez que se reciban nuevos datos.

Se admite la escritura de un valor inicial en un parámetro de componente porque las asignaciones de valores iniciales no interfieren con la representación automática del componente de Blazor. La siguiente asignación de la configuración local DateTime actual de DateTime.Now a StartData es una sintaxis válida en un componente:

[Parameter]
public DateTime StartData { get; set; } = DateTime.Now;

Después de la asignación inicial de DateTime.Now, no asigne un valor a StartData en el código para desarrolladores. Para más información, consulte Evitar sobrescribir parámetros en ASP.NET Core Blazor.

Aplique el [EditorRequired]atributo para especificar un parámetro de componente necesario. Si no se proporciona un valor de parámetro, los editores o las herramientas de compilación pueden mostrar advertencias al usuario. Este atributo solo es válido para las propiedades marcadas también con el [Parameter]atributo. El EditorRequiredAttribute se aplica en tiempo de diseño y cuando se crea la aplicación. El atributo no se aplica en tiempo de ejecución y no garantiza un valor de parámetro distinto a null.

[Parameter]
[EditorRequired]
public string? Title { get; set; }

También se admiten listas de atributos de una sola línea:

[Parameter, EditorRequired]
public string? Title { get; set; }

No use el modificador required ni el descriptor de acceso init en las propiedades del parámetro de componente. Normalmente, los componentes se instancian y se les asignan valores de parámetro mediante reflexión, lo que omite las garantías que ofrecen init y required. En su lugar, use el atributo [EditorRequired] para especificar un parámetro de componente necesario.

No use el descriptor de acceso init en las propiedades del parámetro de componente porque establecer valores de parámetro de componente con ParameterView.SetParameterProperties usa reflexión, lo que omite la restricción del establecedor de solo inicialización. Use el atributo [EditorRequired] para especificar un parámetro de componente necesario.

No use el descriptor de acceso init en las propiedades del parámetro de componente porque establecer valores de parámetro de componente con ParameterView.SetParameterProperties usa reflexión, lo que omite la restricción del establecedor de solo inicialización.

Tuples (documentación de API) se admite para los tipos de RenderFragment y componentes de parámetros. En el ejemplo de parámetro de componente siguiente se pasan tres valores en Tuple:

RenderTupleChild.razor:

<div class="card w-50" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Tuple Card</div>
    <div class="card-body">
        <ul>
            <li>Integer: @Data?.Item1</li>
            <li>String: @Data?.Item2</li>
            <li>Boolean: @Data?.Item3</li>
        </ul>
    </div>
</div>

@code {
    [Parameter]
    public (int, string, bool)? Data { get; set; }
}

RenderTupleParent.razor:

@page "/render-tuple-parent"

<PageTitle>Render Tuple Parent</PageTitle>

<h1>Render Tuple Parent Example</h1>

<RenderTupleChild Data="data" />

@code {
    private (int, string, bool) data = new(999, "I aim to misbehave.", true);
}

Se admiten tuplas con nombre, como se muestra en el ejemplo siguiente:

NamedTupleChild.razor:

<div class="card w-50" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Tuple Card</div>
    <div class="card-body">
        <ul>
            <li>Integer: @Data?.TheInteger</li>
            <li>String: @Data?.TheString</li>
            <li>Boolean: @Data?.TheBoolean</li>
        </ul>
    </div>
</div>

@code {
    [Parameter]
    public (int TheInteger, string TheString, bool TheBoolean)? Data { get; set; }
}

NamedTuples.razor:

@page "/named-tuples"

<PageTitle>Named Tuples</PageTitle>

<h1>Named Tuples Example</h1>

<NamedTupleChild Data="data" />

@code {
    private (int TheInteger, string TheString, bool TheBoolean) data = 
        new(999, "I aim to misbehave.", true);
}

Cita ©2005 Universal Pictures: Serenity (Nathan Fillion)

Tuples (documentación de API) se admite para los tipos de RenderFragment y componentes de parámetros. En el ejemplo de parámetro de componente siguiente se pasan tres valores en Tuple:

RenderTupleChild.razor:

<div class="card w-50" style="margin-bottom:15px">
    <div class="card-header font-weight-bold"><code>Tuple</code> Card</div>
    <div class="card-body">
        <ul>
            <li>Integer: @Data?.Item1</li>
            <li>String: @Data?.Item2</li>
            <li>Boolean: @Data?.Item3</li>
        </ul>
    </div>
</div>

@code {
    [Parameter]
    public (int, string, bool)? Data { get; set; }
}

RenderTupleParent.razor:

@page "/render-tuple-parent"

<h1>Render Tuple Parent</h1>

<RenderTupleChild Data="data" />

@code {
    private (int, string, bool) data = new(999, "I aim to misbehave.", true);
}

Se admiten tuplas con nombre, como se muestra en el ejemplo siguiente:

RenderNamedTupleChild.razor:

<div class="card w-50" style="margin-bottom:15px">
    <div class="card-header font-weight-bold"><code>Tuple</code> Card</div>
    <div class="card-body">
        <ul>
            <li>Integer: @Data?.TheInteger</li>
            <li>String: @Data?.TheString</li>
            <li>Boolean: @Data?.TheBoolean</li>
        </ul>
    </div>
</div>

@code {
    [Parameter]
    public (int TheInteger, string TheString, bool TheBoolean)? Data { get; set; }
}

RenderNamedTupleParent.razor:

@page "/render-named-tuple-parent"

<h1>Render Named Tuple Parent</h1>

<RenderNamedTupleChild Data="data" />

@code {
    private (int TheInteger, string TheString, bool TheBoolean) data = 
        new(999, "I aim to misbehave.", true);
}

Cita ©2005 Universal Pictures: Serenity (Nathan Fillion)

Parámetros de ruta

Los componentes pueden especificar parámetros de ruta en la plantilla de ruta de la directiva @page. El enrutador de Blazor usa parámetros de ruta para rellenar los parámetros de componente correspondientes con el mismo nombre.

RouteParameter1.razor:

@page "/route-parameter-1/{text}"

<PageTitle>Route Parameter 1</PageTitle>

<h1>Route Parameter Example 1</h1>

<p>Blazor is @Text!</p>

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }
}

Para obtener más información, consulte la sección Parámetros de ruta de Enrutamiento y navegación de ASP.NET CoreBlazor. También se admiten parámetros de ruta opcionales y se abordan en la misma sección. Para obtener información sobre los parámetros de ruta comodín ({*pageRoute}), que capturan rutas de acceso en varios límites de carpeta, consulte la sección Parámetros comodín de Enrutamiento y navegación de ASP.NET CoreBlazor.

Para obtener más información, consulte la sección Parámetros de ruta de Enrutamiento y navegación de ASP.NET CoreBlazor. No se admiten parámetros de ruta opcionales, por lo que se requieren dos @page directivas (consulte la sección Parámetros de ruta para obtener más información). Para obtener información sobre los parámetros de ruta comodín ({*pageRoute}), que capturan rutas de acceso en varios límites de carpeta, consulte la sección Parámetros comodín de Enrutamiento y navegación de ASP.NET CoreBlazor.

[¡ADVERTENCIA>] Con la compresión, que está habilitada de forma predeterminada, evite crear componentes interactivos del lado servidor interactivos (autenticados o autorizados) que representen datos de orígenes que no son de confianza. Los orígenes que no son de confianza incluyen parámetros de ruta, cadenas de consulta, datos de interoperabilidad JS y cualquier otro origen de datos que un usuario de terceros pueda controlar (bases de datos, servicios externos). Para obtener más información, consulte Guía de ASP.NETBlazorSignalR y Guía de mitigación de amenazas de representación interactiva del lado servidor para ASP.NET Core Blazor.

Fragmentos de representación de contenido secundario

Los componentes pueden definir el contenido de otro componente. El componente de asignación proporciona el contenido entre las etiquetas de apertura y cierre del componente secundario.

En el siguiente ejemplo, el componente RenderFragmentChild tiene un parámetro de componente ChildContent que representa un segmento de la interfaz de usuario que se va a representar como RenderFragment. La posición de ChildContent en el marcado de Razor del componente es donde el contenido se representa en la salida HTML final.

RenderFragmentChild.razor:

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }
}

Importante

La propiedad que recibe el contenido de RenderFragment debe denominarse ChildContent por convención.

No se admitendevoluciones de llamada de eventos para RenderFragment.

El componente siguiente proporciona contenido para representar el componente RenderFragmentChild mediante la colocación del contenido dentro de las etiquetas de apertura y cierre del componente secundario.

RenderFragments.razor:

@page "/render-fragments"

<PageTitle>Render Fragments</PageTitle>

<h1>Render Fragments Example</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

Debido a la forma en que Blazor representa el contenido secundario, la representación de componentes dentro de un bucle for requiere una variable de índice local si se usa la variable de bucle incremental en el contenido del componente RenderFragmentChild. El ejemplo siguiente se puede agregar al componente primario anterior:

<h1>Three children with an index variable</h1>

@for (int c = 0; c < 3; c++)
{
    var current = c;

    <RenderFragmentChild>
        Count: @current
    </RenderFragmentChild>
}

Como alternativa, use un bucle foreach con Enumerable.Range en lugar de un bucle for. El ejemplo siguiente se puede agregar al componente primario anterior:

<h1>Second example of three children with an index variable</h1>

@foreach (var c in Enumerable.Range(0,3))
{
    <RenderFragmentChild>
        Count: @c
    </RenderFragmentChild>
}

Los fragmentos de representación se usan para representar contenido secundario en todas las aplicaciones de Blazor y se describen con ejemplos en los artículos y secciones siguientes:

Nota

Los componentes Razor integrados del marco de Blazor usan la misma convención de parámetros de componente ChildContent para establecer su contenido. Para ver los componentes que establecen contenido secundario, busque la propiedad de parámetro de componente llamada ChildContent en la documentación de la API (se filtra la API por el término de búsqueda "ChildContent").

Representar fragmentos para la lógica de representación reutilizable

Puede simplificar los componentes secundarios únicamente como forma de reutilizar la lógica de representación. En el bloque @code de cualquier componente, defina una instancia de RenderFragment y represente el fragmento desde cualquier ubicación tantas veces como sea necesario:

@RenderWelcomeInfo

<p>Render the welcome info a second time:</p>

@RenderWelcomeInfo

@code {
    private RenderFragment RenderWelcomeInfo =  @<p>Welcome to your new app!</p>;
}

Para obtener más información, consulte Reutilización de la lógica de representación.

Captura de referencias a componentes

Las referencias de componentes proporcionan una manera de hacer referencia a una instancia de componente para emitir comandos. Para capturar una referencia de componente:

  • Agregue un atributo @ref al componente secundario.
  • Defina un campo con el mismo tipo que el componente secundario.

Cuando el componente se representa, el campo se rellena con la instancia del componente. Tras ello, se pueden invocar métodos de .NET en la instancia.

Tenga en cuenta el componente ReferenceChild siguiente, que registra un mensaje cuando se llama a su método ChildMethod.

ReferenceChild.razor:

@inject ILogger<ReferenceChild> Logger

@if (value > 0)
{
    <p>
        <code>value</code>: @value
    </p>
}

@code {
    private int value;

    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);

        this.value = value;
        StateHasChanged();
    }
}
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> Logger

@code {
    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> Logger

@code {
    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> Logger

@code {
    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> Logger

@code {
    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}

Una referencia de componente solo se rellena después de que el componente se represente, y su salida incluye el elemento de ReferenceChild. Hasta que se represente el componente, no hay nada a lo que hacer referencia. No intente llamar directamente a un método de componente al que se hace referencia para un controlador de eventos (por ejemplo, @onclick="childComponent!.ChildMethod(5)") porque es posible que la variable de referencia no se asigne en el momento en que se asigna el evento de clic.

Para manipular las referencias de componente una vez finalizada la representación del componente, use los métodos OnAfterRender o OnAfterRenderAsync.

En el ejemplo siguiente se usa el componente ReferenceChild anterior.

ReferenceParent.razor:

@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

<div>
    <button @onclick="CallChildMethod">
        Call <code>ReferenceChild.ChildMethod</code> (second instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild? childComponent1;
    private ReferenceChild? childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

<div>
    <button @onclick="CallChildMethod">
        Call <code>ReferenceChild.ChildMethod</code> (second instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild? childComponent1;
    private ReferenceChild? childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

<div>
    <button @onclick="CallChildMethod">
        Call <code>ReferenceChild.ChildMethod</code> (second instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild? childComponent1;
    private ReferenceChild? childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

<div>
    <button @onclick="CallChildMethod">
        Call <code>ReferenceChild.ChildMethod</code> (second instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild childComponent1;
    private ReferenceChild childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

<div>
    <button @onclick="CallChildMethod">
        Call <code>ReferenceChild.ChildMethod</code> (second instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild childComponent1;
    private ReferenceChild childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}

Si bien la captura de referencias de componentes emplea una sintaxis similar a la de la captura de referencias de elementos, esta no es una característica de interoperabilidad de JavaScript. Las referencias de componente no se pasan al código JavaScript. Solo se usan en código .NET.

Importante

No use referencias de componentes para mutar el estado de los componentes secundarios. En su lugar, use parámetros de componente declarativos normales para pasar datos a componentes secundarios. El uso de parámetros de componente da como resultado componentes secundarios que se volverán a representar automáticamente justo cuando corresponda. Para obtener más información, consulte la sección de parámetros del componente y el artículo Enlace de datos de ASP.NET Core Blazor.

Aplicación de un atributo

En los componentes se pueden aplicar atributos con la directiva @attribute. En el siguiente ejemplo se aplica el atributo [Authorize] a la clase del componente:

@page "/"
@attribute [Authorize]

Atributos de elementos HTML condicionales

Las propiedades de atributos de elementos HTML se establecen condicionalmente en función del valor de .NET. Si el valor es false o null, no se establece la propiedad. Si el valor es true, se establece la propiedad.

En el siguiente ejemplo, IsCompleted determina si se establece la propiedad <input> del elemento checked.

ConditionalAttribute.razor:

@page "/conditional-attribute"

<PageTitle>Conditional Attribute</PageTitle>

<h1>Conditional Attribute Example</h1>

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}
@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}
@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}
@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}
@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}

Para obtener más información, consulte la referencia sobre la sintaxis de Razor para ASP.NET Core.

Advertencia

Algunos atributos HTML, como aria-pressed, no funcionan correctamente cuando el tipo de .NET es bool. En esos casos, use un tipo string en lugar de bool.

HTML sin formato

Normalmente, las cadenas se representan mediante nodos de texto DOM, lo que significa que cualquier marcado que esas cadenas puedan contener se omite y se trata como texto literal. Para representar HTML sin formato, encapsule el contenido HTML en un valor MarkupString. El valor se analiza como HTML o SVG y se inserta en el DOM.

Advertencia

La representación de HTML sin formato creado a partir de un origen que no es de confianza entraña un riesgo de seguridad y debe evitarse siempre.

En el siguiente ejemplo se describe cómo usar el tipo MarkupString para agregar un bloque de contenido HTML estático a la salida representada de un componente.

MarkupStrings.razor:

@page "/markup-strings"

<PageTitle>Markup Strings</PageTitle>

<h1>Markup Strings Example</h1>

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

MarkupStringExample.razor:

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

MarkupStringExample.razor:

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

MarkupStringExample.razor:

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

MarkupStringExample.razor:

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

Plantillas de Razor

Los fragmentos de representación se pueden definir mediante la sintaxis de plantilla de Razor para definir un fragmento de interfaz de usuario. Las plantillas de Razor utilizan el siguiente formato:

@<{HTML tag}>...</{HTML tag}>

En el siguiente ejemplo se muestra cómo especificar los valores RenderFragment y RenderFragment<TValue> y las plantillas de representación directamente en un componente. Los fragmentos de representación también se pueden pasar como argumentos a componentes con plantilla.

RazorTemplate.razor:

@page "/razor-template"

<PageTitle>Razor Template</PageTitle>

<h1>Razor Template Example</h1>

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string? Name { get; set; }
    }
}
@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string? Name { get; set; }
    }
}
@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string? Name { get; set; }
    }
}
@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string Name { get; set; }
    }
}
@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string Name { get; set; }
    }
}

Salida representada del código anterior:

<p>The time is 4/19/2021 8:54:46 AM.</p>
<p>Pet: Nutty Rex</p>

Recursos estáticos

Blazor sigue la convención de las aplicaciones de ASP.NET Core para los recursos estáticos. Los recursos estáticos se encuentran en la carpeta web root (wwwroot) del proyecto o en la carpeta wwwroot.

Use una ruta de acceso relativa base (/) para hacer referencia a la raíz web de un activo estático. En el ejemplo siguiente, logo.png se encuentra físicamente en la carpeta {PROJECT ROOT}/wwwroot/images. {PROJECT ROOT} es la raíz del proyecto de la aplicación.

<img alt="Company logo" src="/images/logo.png" />

Los componentes no admiten la notación de virgulilla-barra diagonal (~/).

Para obtener información sobre cómo establecer la ruta de acceso base de una aplicación, consulte Hospedaje e implementación de ASP.NET Core Blazor.

Las aplicaciones auxiliares de etiquetas no se admiten en los componentes

Tag Helpers no se admiten en los componentes. Para proporcionar una funcionalidad similar a la de las aplicaciones auxiliares de etiquetas en Blazor, cree un componente con la misma funcionalidad que la aplicación auxiliar de etiquetas y use el componente en su lugar.

Imágenes con formato Scalable Vector Graphics (SVG)

Como Blazor representa HTML, se pueden usar imágenes compatibles con el explorador, incluidas imágenes con formato Scalable Vector Graphics (SVG) (.svg), mediante la etiqueta <img>:

<img alt="Example image" src="image.svg" />

Del mismo modo, se pueden usar imágenes SVG en las reglas de CSS de un archivo de hoja de estilos (.css):

.element-class {
    background-image: url("image.svg");
}

Blazor admite el elemento <foreignObject> para visualizar HTML arbitrario dentro de un archivo SVG. El marcado puede representar HTML arbitrario, un elemento RenderFragment o un componente Razor.

En el siguiente ejemplo se muestra:

  • Visualización de un elemento string (@message).
  • Enlace bidireccional con un elemento <input> y un campo value.
  • Un componente Robot.
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
    <rect x="0" y="0" rx="10" ry="10" width="200" height="200" stroke="black" 
        fill="none" />
    <foreignObject x="20" y="20" width="160" height="160">
        <p>@message</p>
    </foreignObject>
</svg>

<svg xmlns="http://www.w3.org/2000/svg">
    <foreignObject width="200" height="200">
        <label>
            Two-way binding:
            <input @bind="value" @bind:event="oninput" />
        </label>
    </foreignObject>
</svg>

<svg xmlns="http://www.w3.org/2000/svg">
    <foreignObject>
        <Robot />
    </foreignObject>
</svg>

@code {
    private string message = "Lorem ipsum dolor sit amet, consectetur adipiscing " +
        "elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";

    private string? value;
}

Comportamiento de la representación de espacios en blanco

A menos que se use la directiva @preservewhitespace con un valor de true, el espacio en blanco adicional se quita de forma predeterminada si:

  • Está delante o detrás de un elemento.
  • Está delante o detrás de un parámetro RenderFragment/RenderFragment<TValue> (por ejemplo, el contenido secundario pasado a otro componente).
  • Precede o sigue a un bloque de código de C#, como @if o @foreach.

La eliminación de espacios en blanco puede afectar a la salida representada cuando se usa una regla de CSS, como white-space: pre. Para deshabilitar esta optimización de rendimiento y conservar el espacio en blanco, realice una de las siguientes acciones:

  • Agregue la directiva @preservewhitespace true en la parte superior del archivo de Razor (.razor) para aplicar las preferencias a un componente específico.
  • Agregue la directiva @preservewhitespace true dentro de un archivo _Imports.razor para aplicar la preferencia a un subdirectorio o a todo el proyecto.

En la mayoría de los casos, no se requiere ninguna acción, ya que las aplicaciones normalmente seguirán funcionando con normalidad (pero más rápido). Si la eliminación de espacios en blanco provoca un problema de representación en un componente determinado, use @preservewhitespace true en ese componente para deshabilitar esta optimización.

El espacio en blanco se conserva en el marcado de origen de un componente. El texto de solo espacio en blanco se representa en el modelo DOM del explorador aunque no haya ningún efecto visual.

Considere el siguiente marcado de componente:

<ul>
    @foreach (var item in Items)
    {
        <li>
            @item.Text
        </li>
    }
</ul>

En el ejemplo anterior se representa el siguiente espacio en blanco innecesario:

  • Fuera del bloque de código @foreach.
  • Alrededor del elemento <li>.
  • Alrededor de la salida de @item.Text.

Una lista de 100 elementos da como resultado más de 400 áreas de espacio en blanco. Ninguno de los espacios en blanco adicionales afecta visualmente a la salida representada.

Al representar HTML estático para componentes, no se conservaba el espacio en blanco dentro de una etiqueta. Por ejemplo, vea el resultado representado de la etiqueta <img> siguiente en un archivo de Razor de componente (.razor):

<img     alt="Example image"   src="img.png"     />

No se conserva el espacio en blanco del marcado anterior:

<img alt="Example image" src="img.png" />

Componente raíz

Un componente Razor raíz (componente raíz) es el primer componente cargado de cualquier jerarquía de componentes creada por la aplicación.

En una aplicación creada a partir de la plantilla de proyecto de aplicación web Blazor, el componente App (App.razor) se especifica como componente raíz predeterminado por el parámetro de tipo declarado para la llamada a MapRazorComponents<TRootComponent> en el archivo del lado servidor Program. En el ejemplo siguiente se muestra el uso del componente App como componente raíz, que es el valor predeterminado de una aplicación creada a partir de la plantilla de proyecto Blazor:

app.MapRazorComponents<App>();

Nota:

No se admite la creación de un componente raíz interactivo, como el componente App.

En una aplicación creada a partir de la plantilla de proyecto Blazor Server, el componente App (App.razor) se especifica como el componente raíz predeterminado en Pages/_Host.cshtml mediante el asistente de etiqueta de componente:

<component type="typeof(App)" render-mode="ServerPrerendered" />

En una aplicación creada a partir de la plantilla de proyecto Blazor WebAssembly, el componente App (App.razor) se especifica como el componente raíz predeterminado en el archivo Program:

builder.RootComponents.Add<App>("#app");

En el código anterior, el selector CSS, #app, indica que el componente App se especifica para <div> en wwwroot/index.html con un id de app:

<div id="app">...</app>

Las aplicaciones MVC y Razor Pages también pueden usar el asistente de etiqueta de componente para registrar componentes raíz Blazor WebAssembly representados estáticamente:

<component type="typeof(App)" render-mode="WebAssemblyPrerendered" />

Los componentes representados estáticamente solo se pueden agregar a la aplicación. No se pueden quitar ni actualizar después.

Para obtener más información, vea los siguientes recursos: