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()
usamosOpenComponent()
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 objetoType
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!
Enviar un nuevo comentario