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, 15 de diciembre de 2020
Blazor

En Blazor, es habitual implementar la interfaz de nuestros componentes de forma declarativa, mezclando etiquetas que:

  • Pueden ser HTML, y cuyo resultado irá textualmente al navegador
  • Pueden ser instancias de otros componentes, por lo que al navegador irá el resultado de la renderización de éstos.

Por ejemplo, el siguiente código envía al navegador la etiqueta <h1> y luego el resultado de renderizar el componente Blazor <MyComponent>, en cuyo interior podrán existir a su vez otras etiquetas o componentes:

@page "/home"
<h1>Welcome!<h1>
<MyComponent />

Fijaos que, aunque es la fórmula que emplearemos en la mayoría de ocasiones, es una estructura totalmente rígida: podemos tener la total certeza de que el componente que será instanciado en la página anterior es MyComponent, pero, ¿qué ocurre si queremos poder decidir en tiempo de ejecución qué componente instanciaremos en la página?

La solución: añadir una capa de indirección

David Wheeler decía que cualquier problema en ciencias de la computación puede ser solucionado con otra capa de indirección, y en este caso vamos a demostrarlo creando un componente que será el encargado de crear otros componentes de forma dinámica.

Nuevo objetivo es crear un componente llamado <DynamicComponent> al que podamos suministrar, mediante el parámetro Type (de tipo string), el nombre del componente que deseamos crear en su interior.

Una demostración de su uso podría ser la mostrada a continuación:

<DynamicComponent Type="@component"></DynamicComponent>

<button @onclick='()=>component = "ComponentA"'>A</button>
<button @onclick='()=>component = "ComponentB"'>B</button>
<button @onclick='()=>component = "ComponentC"'>C</button>
@code
{
    string component = "ComponentA";
}

Como podréis ver a continuación, su implementación es bastante sencilla. Dado que se trata de un componente sin interfaz, podemos crearlo directamente en C# heredando de ComponentBase:

public class DynamicComponent : ComponentBase
{
    [Parameter] public string Type { get; set; }

    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        var type = System.Type.GetType(this.GetType().Namespace + "." + Type);
        builder.OpenComponent(1, type);
        builder.CloseComponent();
    }
}

Observad algunas cosas de este código:

  • En el método BuildRenderTree() usamos OpenComponent() para instanciar un componente, cuyo tipo hemos definido dinámicamente en la línea anterior.
  • Dado que para instanciar un tipo por nombre necesitamos su namespace, estamos asumiendo que los componentes instanciados se encuentran en el mismo espacio de nombres que el propio componente DynamicComponent, aunque sería trivial modificar esta convención. También sería muy fácil modificar el componente para usar un objeto Type en lugar de una cadena a la hora de identificar el componente a crear.

Sencillo, ¿verdad?

Pero, ¿y los atributos? ¿Podrían ser también dinámicos?

El ejemplo anterior permitía modificar el tipo de componente instanciado por <DynamicComponent> de forma dinámica, simplemente modificando el nombre del componente en el atributo Type. Sin embargo, no teníamos forma de modificar los atributos de los componentes dinámicos instanciados.

Pero, como podréis ver a continuación, también es bastante fácil de conseguir. Esta nueva versión de DynamicComponent acepta un diccionario en su parámetro Attributes, cuyos valores son añadidos en tiempo de ejecución al crear la instancia del componente dinámico:

public class DynamicComponent : ComponentBase
{
    [Parameter] public string Type { get; set; }
    [Parameter] public Dictionary<string, object> Attributes { get; set; }

    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        var type = System.Type.GetType(this.GetType().Namespace + "." + Type);
        builder.OpenComponent(1, type);
        if (Attributes != null)
        {
            var index = 2;
            foreach (var par in Attributes)
            {
                builder.AddAttribute(index++, par.Key, par.Value);
            }
        }
        builder.CloseComponent();
    }
}

De esta forma, podríamos utilizar DynamicComponent como en el siguiente bloque de código, donde observamos cómo insertar componentes con o sin atributos de forma dinámica, con la pulsación de un botón:

<DynamicComponent Attributes="@attrs" Type="@component"></DynamicComponent>

<button @onclick="WithParameters">Component with params</button>
<button @onclick='WithoutParameters'>Component without params</button>

@code
{
    string component = "ComponentA";
    Dictionary<string, object> attrs;

    private void WithParameters()
    {
        component = "ComponentWithParams";
        attrs = new Dictionary<string, object>()
        {
            ["param1"] = "value1",
            ["param2"] = "value2",
        };
    }

    void WithoutParameters()
    {
        component = "ComponentA";
        attrs = null;
    }
}

¡Espero que os sea de utilidad!

Publicado en Variable not found.

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