Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 22 de septiembre de 2020
Blazor Hoy vamos a hablar de templated components, una interesante capacidad que ofrece Blazor para crear componentes genéricos, capaces de operar con tipos de datos arbitrarios definidos durante la instanciación del componente.

Pero para entender su utilidad, creo que lo mejor es comenzar por un ejemplo de componente simple como el siguiente, que se encarga de mostrar una colección de objetos Friend formateados de una manera determinada:
@* File: ItemList.razor *@
@{
    var count = 1;
    foreach (var item in Items)
    {
        <div class="counter">Item #@(count++)</div>
        <div class="item">
            @ChildContent(item)
        </div>
    }
}

@code
{
    [Parameter]
    public IEnumerable<Friend> Items { get; set; }
    [Parameter]
    public RenderFragment<Friend> ChildContent { get; set; }
}
Fijaos que estamos utilizando un RenderFragment tipado. Si no sabes muy bien lo que es esto, puedes echarle un vistazo al post Componentes con cuerpo en Blazor.
Como hemos podido comprobar, lo único que hace el componente <ItemList> es recorrer la colección de amigos disponible en la propiedad Items y, por cada elemento, mostrar un contador que va incrementándose en cada iteración, renderizando el RenderFragment<Friend> con el ítem actual.

<ItemList> podría ser utilizado desde cualquier otro componente, por ejemplo, de la siguiente manera:
<h1>My friends</h1>

<ItemList Items="@Friends">
    <p>@context.Name is @context.Age years old</p>
</ItemList>

@code {
    public Friend[] Friends { get; set; }

    protected override void OnInitialized()
    {
        Friends = new[] {
            new Friend() {Name = "John", Age = 32}, 
            new Friend() {Name = "Peter", Age = 23}, 
            new Friend() {Name = "Ally", Age = 31}
        };
    }
}
Si ejecutamos un código como el anterior, podremos que ver todo es correcto: la lista de amigos se muestra correctamente. Bueno, todo es correcto, excepto cuando nos preguntamos si este mismo componente podría servir para otros tipos de objeto distintos a Friend.

Esto lo hemos vivido ya antes, muchos años atrás, cuando aún no existían los tipos genéricos en C# y .NET. Porque lo que realmente nos interesaría para lograr una mayor reutilización de código es que el componente <ItemList> fuera genérico y pudiera aplicarse a cualquier tipo de datos.

Templated componentes

Un componente genérico, o templated component, permite la introducción de uno o varios parámetros genéricos que pueden ser luego utilizados en su cuerpo para definir propiedades, miembros u operar con ellos en el código. Estos parámetros genéricos se definen en el componente mediante la directiva @typeparam, asignándole un identificador que luego utilizaremos para referirnos a él.
@typeparam TItem
...
@ code {
    [Parameter]
    public IEnumerable<TItem> Items { get; set; }
}
Observad que el concepto es muy parecido a cuando creamos clases genéricas tradicionales (de hecho, recordad que los componentes Blazor son compilados y se convierten en clases C#):
public class MyClass<TItem>
{
    public IEnumerable<TItem> Items { get; set; }
}
De momento no se permite indicar restricciones o constraints sobre los tipos genéricos de los componentes, aunque está prevista su inclusión en una versión posterior de Blazor.
En el siguiente bloque de código reescribimos el componente <ItemList> de forma genérica. Observad que todas las referencias a Friend podemos reemplazarlas por TItem, el parámetro genérico definido en el componente, y que hemos utilizando la directiva @typeparam para especificar su tipo:
@typeparam TItem
@{
    var count = 1;
    foreach (var item in Items)
    {
        <div class="counter">Item #@(count++)</div>
        <div class="item">
            @ChildContent(item)
        </div>
    }
}

@code
{
    [Parameter]
    public IEnumerable<TItem> Items { get; set; }
    [Parameter]
    public RenderFragment<TItem> ChildContent { get; set; }
}
La forma de utilizar el componente es exactamente la misma, puesto que no hemos cambiado nada hacia fuera. Pero la gran diferencia está en que ahora podríamos utilizar cualquier tipo de colección; el tipo concreto del parámetro TItem es resuelto mediante inferencia en cada caso:
<h1>My friends</h1>
<ItemList Items="@Friends">
    <p>@context.Name is @context.Age years old</p>
</ItemList>

<h1>My colors</h1>
<ItemList Items="@FavoriteColors">
    <p>I like color @context.Name</p>
</ItemList>

@code {
    public Friend[] Friends { get; set; }
    public Color[] FavoriteColors { get; set; }

    protected override void OnInitialized()
    {
        Friends = new Friend[] { ... }; // Omitido
        FavoriteColors = new Color[] { Color.Blue, Color.Green, Color.Red };
        base.OnInitialized();
    }
}
Útil, ¿verdad?
Si te interesa Blazor, ¡no dudes en echar un vistazo a mi curso en CampusMVP!

Publicado en Variable not found.

Aún no hay comentarios, ¡sé el primero!