Compartir a través de


Creación de componentes de interfaz de usuario reutilizables con Blazor

Sugerencia

Este contenido es un extracto del libro electrónico "Blazor for ASP.NET Web Forms Developers for Azure" (Blazor para desarrolladores de ASP.NET Web Forms), disponible en Documentación de .NET o como un PDF descargable y gratuito que se puede leer sin conexión.

Miniatura de la portada del libro electrónico Blazor para desarrolladores de ASP.NET Web Forms.

Uno de los atractivos de ASP.NET Web Forms es cómo permite la encapsulación de fragmentos reutilizables del código de la interfaz de usuario (IU) en controles de interfaz de usuario reutilizables. Los controles de usuario personalizados se pueden definir en el marcado mediante archivos .ascx. También puede crear controles de servidor elaborados en el código con compatibilidad completa con el diseñador.

Blazor también admite la encapsulación de la interfaz de usuario por medio de componentes. Un componente:

  • Es un fragmento independiente de la interfaz de usuario.
  • Mantiene su propio estado y lógica de representación.
  • Puede definir controladores de eventos de la interfaz de usuario, enlazarse a datos de entrada y administrar su propio ciclo de vida.
  • Normalmente se define en un archivo .razor mediante sintaxis de Razor.

Introducción a Razor

Razor es un lenguaje de plantillas de marcado ligero basado en HTML y C#. Con Razor, puede realizar la transición sin problemas entre el marcado y el código de C# para definir la lógica de representación de los componentes. Cuando se compila el archivo .razor, la lógica de representación se captura de forma estructurada en una clase de .NET. El nombre de la clase compilada se toma del nombre del archivo .razor. El espacio de nombres se toma del espacio de nombres predeterminado para el proyecto y la ruta de acceso de la carpeta, o bien se puede especificar de forma explícita mediante la directiva @namespace (a continuación obtendrá más información sobre las directivas de Razor).

La lógica de representación de un componente se crea mediante marcado HTML normal con lógica dinámica agregada por medio de C#. El carácter @ se usa para realizar la transición a C#. Normalmente Razor es una forma inteligente de averiguar cuándo ha cambiado a HTML. Por ejemplo, el componente siguiente representa una etiqueta <p> con la hora actual:

<p>@DateTime.Now</p>

Para especificar de forma explícita el principio y el final de una expresión de C#, use paréntesis:

<p>@(DateTime.Now)</p>

Razor también facilita el uso del flujo de control de C# en la lógica de representación. Por ejemplo, puede representar condicionalmente un código HTML similar al siguiente:

@if (value % 2 == 0)
{
    <p>The value was even.</p>
}

O bien, puede generar una lista de elementos mediante un bucle foreach normal de C# como el siguiente:

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

Las directivas de Razor, como las de ASP.NET Web Forms, controlan muchos aspectos de la compilación de un componente de Razor. Algunos ejemplos son estas partes del componente:

  • Espacio de nombres
  • Clase base
  • Interfaces implementadas
  • Parámetros genéricos
  • Espacios de nombres importados
  • Rutas

Las directivas de Razor comienzan con el carácter @ y se suelen usar al principio de una nueva línea al principio del archivo. Por ejemplo, la directiva @namespace define el espacio de nombres del componente:

@namespace MyComponentNamespace

En la tabla siguiente se resumen las distintas directivas de Razor que se usan en Blazor y sus equivalentes en ASP.NET Web Forms, si existen.

Directiva Descripción Ejemplo Equivalente en Web Forms
@attribute Agrega un atributo de nivel de clase al componente. @attribute [Authorize] None
@code Agrega miembros de clase al componente. @code { ... } <script runat="server">...</script>
@implements Implementa la interfaz especificada. @implements IDisposable Uso de código subyacente
@inherits Hereda de la clase base especificada. @inherits MyComponentBase <%@ Control Inherits="MyUserControlBase" %>
@inject Inserta un servicio en el componente. @inject IJSRuntime JS Ninguno
@layout Especifica un componente de diseño para el componente. @layout MainLayout <%@ Page MasterPageFile="~/Site.Master" %>
@namespace Establece el espacio de nombres para el componente. @namespace MyNamespace None
@page Especifica la ruta del componente. @page "/product/{id}" <%@ Page %>
@typeparam Especifica un parámetro de tipo genérico para el componente. @typeparam TItem Uso de código subyacente
@using Especifica un espacio de nombres que se va a incluir en el ámbito. @using MyComponentNamespace Adición de espacio de nombres en web.config

Los componentes de Razor también hacen un uso intensivo de atributos de directiva en los elementos para controlar diversos aspectos de cómo se compilan los componentes (control de eventos, enlace de datos, referencias de elementos y componentes, etc.). Todos los atributos de directiva siguen una sintaxis genérica común en la que los valores entre paréntesis son opcionales:

@directive(-suffix(:name))(="value")

En la tabla siguiente se resumen los distintos atributos para las directivas de Razor que se usan en Blazor.

Atributo Descripción Ejemplo
@attributes Representa un diccionario de atributos. <input @attributes="ExtraAttributes" />
@bind Crea un enlace de datos bidireccional. <input @bind="username" @bind:event="oninput" />
@on{event} Agrega un controlador de eventos para el evento especificado. <button @onclick="IncrementCount">Click me!</button>
@key Especifica una clave que va a usar el algoritmo de comparación para conservar los elementos de una colección. <DetailsEditor @key="person" Details="person.Details" />
@ref Captura una referencia al componente o elemento HTML. <MyDialog @ref="myDialog" />

Los distintos atributos de directiva utilizados por Blazor (@onclick, @bind, @ref, etc.) se describen en las secciones siguientes y en capítulos posteriores.

Muchas de las sintaxis que se usan en los archivos .aspx y .ascx tienen sintaxis paralelas en Razor. A continuación se muestra una comparación sencilla de las sintaxis para ASP.NET Web Forms y Razor.

Característica formularios Web Forms Sintaxis Razor Sintaxis
Directivas <%@ [directive] %> <%@ Page %> @[directive] @page
Bloques de código <% %> <% int x = 123; %> @{ } @{ int x = 123; }
Expresiones
(Con codificación HTML)
<%: %> <%:DateTime.Now %> Implícitas: @
Explícitas: @()
@DateTime.Now
@(DateTime.Now)
Comentarios <%-- --%> <%-- Commented --%> @* *@ @* Commented *@
Enlace de datos <%# %> <%# Bind("Name") %> @bind <input @bind="username" />

Para agregar miembros a la clase de componente de Razor, use la directiva @code. Esta técnica es similar a usar un bloque <script runat="server">...</script> en una página o control de usuario de ASP.NET Web Forms.

@code {
    int count = 0;

    void IncrementCount()
    {
        count++;
    }
}

Como Razor se basa en C#, se debe compilar desde un proyecto de C# ( .csproj). Los archivos .razor no se pueden compilar desde un proyecto de Visual Basic ( .vbproj). Todavía puede hacer referencia a proyectos de Visual Basic desde el proyecto Blazor. Lo contrario también es cierto.

Para obtener una referencia completa de la sintaxis de Razor, vea Referencia de sintaxis de Razor para ASP.NET Core.

Uso de componentes

Además de código HTML normal, los componentes también pueden usar otros componentes como parte de su lógica de representación. La sintaxis para utilizar un componente en Razor es similar al uso de un control de usuario en una aplicación de ASP.NET Web Forms. Los componentes se especifican mediante una etiqueta de elemento que coincide con el nombre de tipo del componente. Por ejemplo, puede agregar un componente Counter de esta forma:

<Counter />

A diferencia de ASP.NET Web Forms, los componentes de Blazor:

  • No usan un prefijo de elemento (por ejemplo, asp:).
  • No es necesario registrarlos en la página ni en web.config.

Piense en los componentes de Razor como si fueran tipos de .NET, ya que son exactamente eso. Si se hace referencia al ensamblado que contiene el componente, el componente está disponible para su uso. Para poner el espacio de nombres del componente en el ámbito, aplique la directiva @using:

@using MyComponentLib

<Counter />

Como se ve en los proyectos de Blazor predeterminados, es habitual colocar las directivas @using en un archivo _Imports.razor para que se importen en todos los archivos .razor en el mismo directorio y en los directorios secundarios.

Si el espacio de nombres de un componente no está en el ámbito, puede especificar un componente con su nombre de tipo completo, como en C#:

<MyComponentLib.Counter />

Modificación del título de páginas a partir de componentes

Al compilar aplicaciones de estilo SPA, es habitual que las partes de una página se vuelvan a cargar sin volver a cargar toda la página. Aun así, puede ser útil tener el título de la página cambiado en función del componente que se carga actualmente. Para ello, incluya la etiqueta <PageTitle> en la página de Razor del componente:

@page "/"
<PageTitle>Home</PageTitle>

El contenido de este elemento puede ser dinámico, por ejemplo, mostrando el recuento actual de mensajes:

<PageTitle>@MessageCount messages</PageTitle>

Tenga en cuenta que si varios componentes de una página determinada incluyen etiquetas <PageTitle>, solo se mostrará el último (ya que cada uno sobrescribirá el anterior).

Parámetros del componente

En ASP.NET Web Forms, los parámetros y los datos se pueden enviar a los controles mediante propiedades públicas. Estas propiedades se pueden establecer en el marcado mediante atributos, o bien establecerse directamente en el código. Los componentes de Razor funcionan de manera similar, aunque las propiedades del componente también se deben marcar con el atributo [Parameter] para que se consideren parámetros de componente.

El componente Counter siguiente define un parámetro de componente denominado IncrementAmount que se puede usar para especificar la cantidad que se debe incrementar Counter cada vez que se haga clic en el botón.

<h1>Counter</h1>

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

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

@code {
    int currentCount = 0;

    [Parameter]
    public int IncrementAmount { get; set; } = 1;

    void IncrementCount()
    {
        currentCount+=IncrementAmount;
    }
}

Para especificar un parámetro de componente en Blazor, use un atributo como haría en ASP.NET Web Forms:

<Counter IncrementAmount="10" />

Parámetros de cadena de consulta

Los componentes de Razor también pueden sacar provecho de los valores de la cadena de consulta, que están en la página en la que se representan como el origen de un parámetro. Para habilitar esto, agregue el atributo [SupplyParameterFromQuery] al parámetro. Por ejemplo, la siguiente definición de parámetro obtendría su valor de la solicitud con el formato ?IncBy=2:

[Parameter]
[SupplyParameterFromQuery(Name = "IncBy")]
public int IncrementAmount { get; set; } = 1;

Si no proporciona un elemento Name personalizado en el atributo [SupplyParameterFromQuery], de forma predeterminada coincidirá con el nombre de la propiedad (IncrementAmount en este caso).

Componentes y límites de errores

De forma predeterminada, las aplicaciones de Blazor detectarán excepciones no controladas y mostrarán un mensaje de error en la parte inferior de la página sin detalles adicionales. Para restringir las partes de la aplicación que se ven afectadas por un error no controlado, por ejemplo, para limitar el impacto a un único componente, la etiqueta <ErrorBoundary> se puede encapsular alrededor de las declaraciones del componente.

Por ejemplo, para protegerse frente a posibles excepciones producidas por el componente Counter, declárelo dentro de <ErrorBoundary> y, opcionalmente, especifique un mensaje para mostrar si hay una excepción:

<ErrorBoundary>
    <ChildContent>
        <Counter />
    </ChildContent>
    <ErrorContent>
        Oops! The counter isn't working right now; please try again later.
    </ErrorContent>
</ErrorBoundary>

Si no necesita especificar contenido de error personalizado, solo tiene que encapsular el componente directamente:

<ErrorBoundary>
  <Counter />
</ErrorBoundary>

Se mostrará un mensaje predeterminado con el texto "Se ha producido un error." si se produce una excepción no controlada en el componente ajustado.

Controladores de eventos

Tanto en ASP.NET Web Forms como en Blazor se proporciona un modelo de programación basado en eventos para controlar los eventos de la interfaz de usuario. Entre los ejemplos de estos eventos se incluyen los clics de botón y la entrada de texto. En ASP.NET Web Forms, se usan controles de servidor HTML para controlar los eventos de la interfaz de usuario expuestos por el DOM, o bien se pueden controlar los expuestos por controles de servidor web. Los eventos se muestran en el servidor mediante solicitudes de reenvío de formulario. Considere el ejemplo siguiente de clic de botón de Web Forms:

Counter.ascx

<asp:Button ID="ClickMeButton" runat="server" Text="Click me!" OnClick="ClickMeButton_Click" />

Counter.ascx.cs

public partial class Counter : System.Web.UI.UserControl
{
    protected void ClickMeButton_Click(object sender, EventArgs e)
    {
        Console.WriteLine("The button was clicked!");
    }
}

En Blazor, puede registrar controladores para eventos de interfaz de usuario del DOM directamente using atributos de directiva con el formato @on{event}. El marcador de posición {event} representa el nombre del evento. Por ejemplo, puede realizar escuchas de clics de botón de esta manera:

<button @onclick="OnClick">Click me!</button>

@code {
    void OnClick()
    {
        Console.WriteLine("The button was clicked!");
    }
}

Los controladores de eventos pueden aceptar un argumento opcional específico del evento para proporcionar más información sobre el evento. Por ejemplo, los eventos del mouse pueden tomar un argumento MouseEventArgs, pero no es necesario.

<button @onclick="OnClick">Click me!</button>

@code {
    void OnClick(MouseEventArgs e)
    {
        Console.WriteLine($"Mouse clicked at {e.ScreenX}, {e.ScreenY}.");
    }
}

En lugar de hacer referencia a un grupo de métodos para un controlador de eventos, puede usar una expresión lambda. Una expresión lambda permite cerrar otros valores en el ámbito.

@foreach (var buttonLabel in buttonLabels)
{
    <button @onclick="() => Console.WriteLine($"The {buttonLabel} button was clicked!")">@buttonLabel</button>
}

Los controladores de eventos se pueden ejecutar de forma sincrónica o asincrónica. Por ejemplo, el controlador de eventos OnClick siguiente se ejecuta de forma asincrónica:

<button @onclick="OnClick">Click me!</button>

@code {
    async Task OnClick()
    {
        var result = await Http.GetAsync("api/values");
    }
}

Una vez que se ha controlado un evento, el componente se representará para tener en cuenta los cambios de estado del componente. Con los controladores de eventos asincrónicos, el componente se representa inmediatamente después de que se complete la ejecución del controlador. El componente se representará de nuevo después de que se complete el objeto Task asincrónico. Este modo de ejecución asincrónica proporciona una oportunidad para representar una interfaz de usuario adecuada mientras el objeto Task asincrónico todavía está en curso.

<button @onclick="ShowMessage">Get message</button>

@if (showMessage)
{
    @if (message == null)
    {
        <p><em>Loading...</em></p>
    }
    else
    {
        <p>The message is: @message</p>
    }
}

@code
{
    bool showMessage = false;
    string message;

    public async Task ShowMessage()
    {
        showMessage = true;
        message = await MessageService.GetMessageAsync();
    }
}

Los componentes también pueden definir eventos propios si definen un parámetro de componente de tipo EventCallback<TValue>. Las devoluciones de llamada de evento admiten todas las variaciones de los controladores de eventos de interfaz de usuario del DOM: argumentos opcionales, sincrónicos o asincrónicos, grupos de métodos o expresiones lambda.

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

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClick { get; set; }
}

Enlace de datos

Blazor proporciona un mecanismo sencillo para enlazar datos de un componente de interfaz de usuario al estado del componente. Este enfoque difiere de las características de ASP.NET Web Forms para enlazar datos desde orígenes de datos a controles de interfaz de usuario. En la sección Tratamiento de los datos se describirá el control de datos de orígenes de datos diferentes.

Para crear un enlace de datos bidireccional desde un componente de interfaz de usuario al estado del componente, use el atributo de directiva @bind. En el ejemplo siguiente, el valor de la casilla está enlazado al campo isChecked.

<input type="checkbox" @bind="isChecked" />

@code {
    bool isChecked;
}

Cuando se representa el componente, el valor de la casilla se establece en el del campo isChecked. Cuando el usuario activa la casilla, se desencadena el evento onchange y el campo isChecked se establece en el nuevo valor. En este caso, la sintaxis @bind es equivalente al marcado siguiente:

<input value="@isChecked" @onchange="(UIChangeEventArgs e) => isChecked = e.Value" />

Para cambiar el evento utilizado para el enlace, use el atributo @bind:event.

<input @bind="text" @bind:event="oninput" />
<p>@text</p>

@code {
    string text;
}

Los componentes también pueden admitir el enlace de datos a sus parámetros. Para enlazar datos, defina un parámetro de devolución de llamada de evento con el mismo nombre que el parámetro enlazable. El sufijo "Changed" se agrega al nombre.

PasswordBox.razor

Password: <input
    value="@Password"
    @oninput="OnPasswordChanged"
    type="@(showPassword ? "text" : "password")" />

<label><input type="checkbox" @bind="showPassword" />Show password</label>

@code {
    private bool showPassword;

    [Parameter]
    public string Password { get; set; }

    [Parameter]
    public EventCallback<string> PasswordChanged { get; set; }

    private Task OnPasswordChanged(ChangeEventArgs e)
    {
        Password = e.Value.ToString();
        return PasswordChanged.InvokeAsync(Password);
    }
}

Para encadenar un enlace de datos a un elemento de la interfaz de usuario subyacente, establezca el valor y controle el evento directamente en el elemento de la interfaz de usuario en lugar de usar el atributo @bind.

Para enlazar a un parámetro de componente, use un atributo @bind-{Parameter} para especificar el parámetro al que quiera enlazar.

<PasswordBox @bind-Password="password" />

@code {
    string password;
}

Cambios de estado

Si el estado del componente ha cambiado fuera de un evento de interfaz de usuario normal o una devolución de llamada de evento, el componente debe indicar manualmente que se debe volver a representar. Para indicar que el estado de un componente ha cambiado, llame al método StateHasChanged en el componente.

En el ejemplo siguiente, un componente muestra un mensaje de un servicio AppState que otras partes de la aplicación pueden actualizar. El componente registra su método StateHasChanged con el evento AppState.OnChange para que se represente el componente cada vez que se actualice el mensaje.

public class AppState
{
    public string Message { get; }

    // Lets components receive change notifications
    public event Action OnChange;

    public void UpdateMessage(string message)
    {
        Message = message;
        NotifyStateChanged();
    }

    private void NotifyStateChanged() => OnChange?.Invoke();
}
@inject AppState AppState

<p>App message: @AppState.Message</p>

@code {
    protected override void OnInitialized()
    {
        AppState.OnChange += StateHasChanged
    }
}

Ciclo de vida del componente

El marco ASP.NET Web Forms tiene métodos de ciclo de vida bien definidos para módulos, páginas y controles. Por ejemplo, el control siguiente implementa controladores de eventos para los eventos de ciclo de vida Init, Load y UnLoad:

Counter.ascx.cs

public partial class Counter : System.Web.UI.UserControl
{
    protected void Page_Init(object sender, EventArgs e) { ... }
    protected void Page_Load(object sender, EventArgs e) { ... }
    protected void Page_UnLoad(object sender, EventArgs e) { ... }
}

Los componentes de Razor también tienen un ciclo de vida bien definido. El ciclo de vida de un componente se puede usar para inicializar el estado del componente e implementar comportamientos de componentes avanzados.

Todos los métodos de ciclo de vida del componente Blazor tienen versiones sincrónicas y asincrónicas. La representación de componentes es sincrónica. No se puede ejecutar lógica asincrónica como parte de la representación de componentes. Toda la lógica asincrónica se debe ejecutar como parte de un método de ciclo de vida async.

OnInitialized

Los métodos OnInitialized y OnInitializedAsync se usan para inicializar el componente. Normalmente, un componente se inicializa después de que se represente por primera vez. Una vez que se ha inicializado un componente, se puede representar varias veces antes de eliminarlo. El método OnInitialized es similar al evento Page_Load en las páginas y controles de ASP.NET Web Forms.

protected override void OnInitialized() { ... }
protected override async Task OnInitializedAsync() { await ... }

OnParametersSet

Los métodos OnParametersSet y OnParametersSetAsync se llaman cuando un componente ha recibido parámetros de su elemento primario y el valor se asigna a propiedades. Estos métodos se ejecutan después de la inicialización del componente y cada vez que se representa.

protected override void OnParametersSet() { ... }
protected override async Task OnParametersSetAsync() { await ... }

OnAfterRender

Los métodos OnAfterRender y OnAfterRenderAsync se llaman después de que un componente haya terminado de representarse. En este punto se rellenan las referencias de elementos y componentes (a continuación obtendrá más información sobre estos conceptos). En este momento se habilita la interactividad con el explorador. Las interacciones con el DOM y la ejecución de JavaScript pueden tener lugar de forma segura.

protected override void OnAfterRender(bool firstRender)
{
    if (firstRender)
    {
        ...
    }
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        await ...
    }
}

OnAfterRender y OnAfterRenderAsyncno son llamados cuando se representan previamente en el servidor.

El parámetro firstRender es true la primera vez que se representa el componente; de lo contrario, su valor es false.

IDisposable

Los componentes de Razor pueden implementar IDisposable para desechar recursos cuando el componente se elimina de la UI. Un componente de Razor puede implementar IDispose mediante la directiva @implements:

@using System
@implements IDisposable

...

@code {
    public void Dispose()
    {
        ...
    }
}

Captura de referencias a componentes

En ASP.NET Web Forms, es habitual manipular una instancia de control directamente en el código haciendo referencia a su identificador. En Blazor, también es posible capturar y manipular una referencia a un componente, aunque es mucho menos frecuente.

Para capturar una referencia de componente en Blazor, use el atributo de directiva @ref. El valor del atributo debe coincidir con el nombre de un campo configurable con el mismo tipo que el componente al que se hace referencia.

<MyLoginDialog @ref="loginDialog" ... />

@code {
    MyLoginDialog loginDialog = default!;

    void OnSomething()
    {
        loginDialog.Show();
    }
}

Cuando se representa el componente primario, el campo se rellena con la instancia del componente secundario. Después, puede llamar a métodos en la instancia del componente o manipularla de otro modo.

No se recomienda manipular el estado del componente directamente mediante referencias de componentes. Al hacerlo, se impide que el componente se represente de forma automática en los momentos correctos.

Captura de referencias a elementos

Los componentes de Razor pueden capturar referencias a un elemento. A diferencia de los controles de servidor HTML en ASP.NET Web Forms, no se puede manipular el DOM directamente mediante una referencia de elemento en Blazor. Blazor controla de forma automática la mayoría de las interacciones del DOM con su algoritmo de diferenciación del DOM. Las referencias de elemento capturadas en Blazor son opacas. Pero se usan para pasar una referencia de elemento específica en una llamada de interoperabilidad de JavaScript. Para obtener más información sobre la interoperabilidad de JavaScript, vea Interoperabilidad de JavaScript para Blazor en ASP.NET Core.

Componentes con plantilla

En ASP.NET Web Forms, puede crear controles basados en modelo. Los controles basados en modelo permiten al desarrollador especificar una parte del código HTML que se usa para representar un control de contenedor. La mecánica de la creación de controles basados en modelo de servidor es compleja, pero permiten escenarios eficaces para representar los datos de forma personalizable. Entre los ejemplos de controles basados en modelo se incluyen Repeater y DataList.

Los componentes de Razor también se pueden basar en modelo si se definen parámetros de componente de tipo RenderFragment o RenderFragment<T>. RenderFragment representa un fragmento de marcado de Razor que se puede representar mediante el componente. RenderFragment<T> es un fragmento de marcado de Razor que toma un parámetro que se puede especificar cuando se representa el fragmento de representación.

Contenido secundario

Los componentes de Razor pueden capturar su contenido secundario como RenderFragment y representarlo como parte de la representación de componentes. Para capturar contenido secundario, defina un parámetro de componente de tipo RenderFragment y asígnele el nombre ChildContent.

ChildContentComponent.razor

<h1>Component with child content</h1>

<div>@ChildContent</div>

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

Después, un componente primario puede proporcionar contenido secundario mediante la sintaxis normal de Razor.

<ChildContentComponent>
    <ChildContent>
        <p>The time is @DateTime.Now</p>
    </ChildContent>
</ChildContentComponent>

Parámetros de plantilla

Un componente de Razor basado en modelo también puede definir varios parámetros de componente de tipo RenderFragment o RenderFragment<T>. El parámetro de RenderFragment<T> se puede especificar durante la invocación. Para especificar un parámetro de tipo genérico para un componente, use la directiva @typeparam de Razor.

SimpleListView.razor

@typeparam TItem

@Heading

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

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

    [Parameter]
    public RenderFragment<TItem> ItemTemplate { get; set; }

    [Parameter]
    public IEnumerable<TItem> Items { get; set; }
}

Al usar un componente con plantilla, los parámetros de plantilla se pueden especificar utilizando los elementos secundarios que coinciden con los nombres de los parámetros. Los argumentos de componente de tipo RenderFragment<T> pasados como elementos tienen un parámetro implícito denominado context. Puede cambiar el nombre de este parámetro de implementación mediante el atributo Context en el elemento secundario. Se puede especificar cualquier parámetro de tipo genérico mediante un atributo que coincida con el nombre del parámetro de tipo. Si es posible, el parámetro de tipo se infiere:

<SimpleListView Items="messages" TItem="string">
    <Heading>
        <h1>My list</h1>
    </Heading>
    <ItemTemplate Context="message">
        <p>The message is: @message</p>
    </ItemTemplate>
</SimpleListView>

La salida de este componente tiene el siguiente aspecto:

<h1>My list</h1>
<ul>
    <li><p>The message is: message1</p></li>
    <li><p>The message is: message2</p></li>
<ul>

Código subyacente

Normalmente, un componente de Razor se crea en un solo archivo .razor. Pero también es posible separar el código y el marcado mediante un archivo de código subyacente. Para usar un archivo de componente, agregue un archivo de C# que coincida con el nombre del archivo de componente, pero con una extensión .cs agregada (Counter.razor.cs). Use el archivo de C# para definir una clase base para el componente. Puede asignar a la clase base el nombre que prefiera aunque es habitual usar uno que sea igual a la clase de componente, pero con una extensión Base agregada (CounterBase). La clase basada en el componente también se debe derivar de ComponentBase. Después, en el archivo de componente de Razor, agregue la directiva @inherits para especificar la clase base del componente (@inherits CounterBase).

Counter.razor

@inherits CounterBase

<h1>Counter</h1>

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

<button @onclick="IncrementCount">Click me</button>

Counter.razor.cs

public class CounterBase : ComponentBase
{
    protected int currentCount = 0;

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

La visibilidad de los miembros del componente en la clase base debe ser protected o public para que sea visible para la clase de componente.

Recursos adicionales

Las secciones anteriores no son un tratamiento exhaustivo de todos los aspectos de los componentes de Razor. Para obtener más información sobre cómo Crear y usar componentes de Razor en ASP.NET Core, vea la documentación de Blazor.