Interpolación de cadenas y binding de propiedades

En un artículo anterior hemos mencionado la interpolación de cadenas. En este artículo vamos a volver sobre este tema, para ver como podermos pasar datos de la lógica de negocio de nuestra apliccación (archivos .ts) a las vistas (archivos .html).

El paso de estos datos no se limita sólo a valores para mostrar, sino que va bastante más alla. Podemos hacer que las propiedades de una etiqueta HTML tengan un valor que se genere en la lógica de negocio, de forma que, dependiendo de lo que haya en el archivo TypeScript la etiqueta HTML se comporte de distinto modo.

Esto es lo que se conoce como binding de propiedades, es decir, relacionar el valor de una propiedad con una variable definida en otra parte.

LA ESTRUCTURA DE UN COMPONENTE

Aunque a estas alturas ya estamos bastante familiarizados con la estructura de un componente, vamos a reparar en el hecho de que, para hablar de este tema vamos a fijarnos en dos de los archivos que lo conforman:

  • Uno es el que gestiona la lógica de negocio, es decir, el comportamiento, o la operativa del componente. Se trata del que tiene el nombre del componente, seguido de un punto y la partícula component, y la extensión .ts. Por ejemplo, como hemos manejado de momento, app.component.ts o header.component.ts.
  • El otro es el que tiene un código HTML que renderiza en el navegador. Su nombre está formado por el nombre del componente, un punto, la partícula component y la extensión .html, como es el caso de app.component.html o header.component.html.

La cuestión es pasar datos del archivo que gestiona la lógica al que renderiza contenidos, de modo que los contenidos que se rendericen puedan variar dependiendo de los datos pasados por el archivo typescript. Ya vimos, de pasada, este tema en el artículo que hemos mencionado antes, pero ahora vamos a estructurar este conocimiento. En realidad, lo que vamos a ver aquí no es nuevo, pero sí constituye la base para el tema del que hablaremos en el siguiente artículo, así que este repaso nos viene fenomenal.

LAS LÓGICAS DE NEGOCIO

Vamos a ver la lógica de negocio del HeaderComponent que tenemos. Parece un poco pretencioso hablar de «logica de negocio» en un componente que es, sólamente, una cabecera de lo más sencilla, pero la estructura básica de los componentes en Angular se mantiene siempre, desde un simple «Hola Mundo» hasta la aplicación más compleja que puedas imaginar. Nuestro archivo header.component.ts responde al siguiente listado:

La línea 1 contiene las importaciones básicas. Si vamos a hacer otras importaciones desde otros elementos de Angular añadiremos más líneas, como veremos en su momento. Por ahora, tiene las que se han creado por defecto al generar el componente, y con esas nos vale.

Las líneas de la 3 a la 7 contienen lo que ya conocemos como el decorador del componente. En el vemos que tenemos tres propiedades:

  • La que está identificada por la clave selector, que tiene como valor una etiqueta que sabemos que luego usaremos para colocar este componente donde nos haga falta. En este caso, el valor de esta propiedad es a01-header que, como ya sabes, coincide con la etiqueta <a01-header></a01-header> en app.component.html, para que la cabecera se incluya en el componente.
  • La que está identificada con la clave templateUrl, cuyo valor corresponde con el fichero HTML del propio componente. Es a través de esta propiedad que ambos elementos del componente están relacionados.
  • La que está identificada con la clave styleUrls, que contiene una matriz con la lista de archivos de estilos que se emplean en el componente.

Esta estructura ya la habíamos mencionado de pasada en un artículo anterior, pero ahora, que ya vamos teniendo las ideas más claras, nos viene bien comentarlo para afianzar esta estructura en nuestra mente.

Por último, las líneas de la 9 a la 16 exportan el componente como una clase (ya sabemos que, en Angular 5 todo son clases), implementando la interfaz onInit que, por ahora, no le damos ningún uso, pero que se lo daremos en su momento. Vemos que la clase también incorpora un método constructor que, por ahora, está vacío.

En el caso del componente principal, la logica de negocio (app.component.ts) es aún más simple si cabe:

Aunque los tres bloques (importación, decorador y exportación de la clase) son los mismos, vemos que aquí, por ahora, no se importa la interfaz onInit, ni se emplea en la declaración de la clase que se exporta. Lo único importante, por ahora, es ver que se mantiene la estructura básica del script y, específicamente, del decorador.

DECLARANDO VALORES

Vamos a declarar algunos valores en la lógica de negocio de ambos componentes, de modo que luego podamos usarlos en las correspondientes plantillas HTML. Empezaremos por valores que vamos a declarar en header.component.ts:

Observa las líneas resaltadas. Tenemos declaradas dos variables o, mejor dicho, dos propiedades (una con un valor literal y otra con un numérico) en la clase que se exporta. Ahora modificamos el HTML para poder recuperar esas variables, quedando el header.component.html así:

Ahora vamos a hacer algo parecido en AppComponent. En primer lugar declaramos unos valores en la clase que se exporta desde la lógica de negocio, como vemos en app.component.ts:

Observa que hemos definido un par de URL’s (a la postre, un par de cadenas literales) pero, esta vez, no las hemos definido como variables independientes, sino como elementos de una matriz. Esto es perfectamente legítimo aunque, cómo es lógico, deberemos tener esto en cuenta cuando vayamos a usar estos valores en la plantilla HTML. El archivo app.component.html podría quedar así:

Observa, en las líneas resaltadas, como hemos usado las variables de la matriz que llega desde el typescript para construir dos enlaces. Si ahora observas tu aplicación en el navegador, verás algo cómo lo siguiente:

Como ves, lo importante es que los valores declarados en la lógica de negocio se invoquen en la vista encerrados entre dobles llaves ({{ y }}).

ATENCIÓN. En el segundo ejemplo, donde hemos creado los enlaces, realmente lo que estamos usando no es, estrictamente hablando, interpolación de variables. Dado que las variables las empleamos en las propiedades de etiquetas HTML, esto se conoce como Binding de propiedades. Es una técnica que resulta muy útil para determinados diseños. No obstante, la mecánica es la misma: definimos un valor en el typescript y lo invocamos, con la sintaxis de las dobles llaves, en el HTML.

BINDING DE PROPIEDADES

Como ves, el binding de propiedades consiste en usar la misma técnica de interpolación de variables, pero aplicada a una propiedad de una etiqueta HTML para otorgarle un valor. Sin embargo, el binding de propiedades admite otra sintaxis, de la que vamos a hablar a continuación. Consiste en encerrar el nombre de la propiedad entre corchetes ([ y ]), y suprimir las dobles llaves en el nombre de la variable que contiene el valor que queremos insertar. En este ejemplo, aunque app.component.ts no sufre ya más modificación, si encontramos la diferencia en app.component.html:

Si lo ponemos como ves en el ejemplo, el funcionamiento es exactamente el mismo. De esto se deduce que el binding de propiedades admite dos sintaxis:

  • La que usamos en el primer ejemplo, en la que la variable que contiene el valor de la propiedad se encierra entre dobles llaves. Esta sintaxis coincide con la de la interpolación de variables.
  • La que usamos en el segundo ejemplo, en la que el nombre de la propiedad se encierra entre cochetes, y la variable se incluye como un literal, SIN las dobles llaves. Usando esta segunda sintaxis, si encerrásemos el valor con dobles llaves se produciría un error de ejecución. Es importante no mezclar las dos sintaxis.

Y, claro. Surge la pregunta ¿Cuándo usamos una sintaxis u otra? Bien. En la mayoría de los casos, el binding de propiedades puede usarse indistintamente con una sintaxis u otra. Sin embargo, hay algún caso en que deberemos usar la segunda sintaxis, como vemos en el siguiente apartado.

SINTAXIS ESPECÍFICA PARA BINDING DE PROPIEDADES

Cómo hemos comentado, en la mayoría de los casos el binding de propiedades se puede hacer con una sintaxis o con la otra, indistintamente. A título personal, prefiero la segunda (la que encierra el nombre de la propiedad entre cochetes), porque el código queda más claro y legible. Sin embargo, hay casos en que nos veremos obligados a usar, sí o sí, esta última sintaxis, no pudiendo recurrir a la primera.

La cuestión es la siguiente: la mayoría de las propiedades de etiquetas HTML las afectan de una u otra forma en base al valor que tengan. Es el caso, por ejemplo, de los atributos href de la etiqueta <a>. Según el valor que le demos, el enlace nos conducirá a una página u otra. Sin embargo, algunas propiedades específicas se comportan de forma diferente, dependiendo, simplemente, de que estén declaradas o no o, desde el punto de vista del DOM, de que tengan un valor true o false. Es el caso, por ejemplo, de la propiedad disabled en un botón. Cuando escribimos HTML nos basta poner el botón con la propiedad disabled, sin atribuirle ningún valor, para que el botón esté desactivado. Desde JavaScript podemos modificar esa propiedad, asignándole el valor booleano true, con lo que el botón estará desactivado, o false, con lo que ignorará la propiedad disabled y estará activado. Es decir. Por el mero hecho de declarar la propiedad disabled en el botón en HTML ya se asume que su valor es true, por defecto, aunque no lo indiquemos específicamente. Todo esto no es nada nuevo. Es HTML y JavaScript básico, y lo conoces perfectamente.

Vamos a suponer que tenemos, en nuestro app.component.html un botón como el siguiente:

<button class='btn btn-primary' onClick='alert("Has pulsado el botón");'>Botón de ejemplo</button>

Tendrás en tu página un botón que, al ser pulsado, te muestra un alert de JavaScript. Perfecto. Ahora lo ponemos así:

<button class='btn btn-primary' onClick='alert("Has pulsado el botón");' disabled>Botón de ejemplo</button>

Tendrás en tu página un botón que, al ser pulsado, no hace nada, porque no puedes pulsarlo, ya que tiene la propiedad disabled añadida, con lo que se le asume, por defecto, el valor true, e inhabilita el botón.

Ahora vamos a añadir, en app.component.ts una variable que contenga un valor booleano, así:

boton_desactivado = true;

Y modificamos el app.component.html, para que la propiedad disabled del botón recoja el valor de la variable, así:

<button class='btn btn-primary' onClick='alert("Has pulsado el botón");' [disabled]='boton_desactivado'>Botón de ejemplo</button>

Como la variable boton_desactivado vale true, le estamos dando, a la propiedad disabled, el valor true de forma específica, con lo que el botón sigue inhabilitado. Sin embargo fíjate que el nombre de la propiedad disabled lo hemos encerrado entre corchetes, con lo que estamos haciendo binding de esta propiedad. Le estamos diciendo que tiene que asumir el valor que le llega del TypeScript. Si en app.component.ts cambias el valor true por false en la variable boton_desactivado, la propiedad disabled estará recibiendo el valor false, y el botón estará habilitado. Hay otra forma de habilitar el botón. Mantenemos en el TypeScript la variable boton_desactivado a true, y negamos su contenido en app.component.html, con el operador de negación (!), de forma que el botón quedará definido así:

<button class='btn btn-primary' onClick='alert("Has pulsado el botón");' [disabled]='!boton_desactivado'>Botón de ejemplo</button>

Como ves, este binding de propiedades funciona perfectamente.

Y ¿POR QUÉ NO LA PRIMERA SINTAXIS?

Esa es la cuestión. ¿Por qué no usar para este binding de propiedades la primera sintaxis, la que emplea las dobles llaves y se parece a la interpolación de variables? Pues porque si lo que hacemos es declarar la propiedad y pasarle un valor, sea el que sea, con la sintaxis de interpolación de propiedades, no se va a asumir como un valor booleano, sino como una cadena. Esta es una característica inherente a la interpolación de propiedades. Al asumirse como una cadena, contenga lo que contenga, en una propiedad que espera un valor booleano, se asume, implícitamente, como true. Suponte que tienes tu app.component.ts así:

Observa, en la línea resaltada, cómo hemos declarado la variable boton_desactivado como un false booleano. Ahora vamos a tratar de usar el binding de propiedades en app.component.html, para que el botón aparezca habilitado, así:

Observa la línea resaltada. Como le estamos asignando el valor a disabled con la sintaxis de interpolación, declarar así el botón equivale a haberlo declarado así:

<button class='btn btn-primary' onClick='alert("Has pulsado el botón");' disabled='false'>Botón de ejemplo</button>

Y, en este caso, 'false' no es el valor lógico false, sino una cadena literal, lo que, por tanto, se asume como un valor lógico true, que no es lo que queremos.

POR LO TANTO. Por tanto, si vamos a usar binding de propiedades que requieran específicamente un valor booleano, siempre usaremos la sintaxis de los corchetes, no la de las llaves.
ATENCIÓN. Lo que hemos hecho con el botón, de asignarle una instrucción JavaScript al evento onClick no es la forma correcta de hacer las cosas en Angular 5. Como la operativa (todo el JavaScript) debe estar en el archivo que gestiona la lógica de negocio, y no en el HTML, emplearemos una técnica conocida como binding de eventos. En este ejemplo lo hemos hecho así para simplificar, pero, en el trabajo real, nunca haremos esto. En el próximo artículo hablaremos de binding de eventos.

CONCLUYENDO

En este artículo hemos aprendido mucho acerca de como se comunican datos entre el TypeScript y el HTML en un desarrollo en Angular 5. Evidentemente (y sé que lo has pensado), los valores de las variables en el TypeScript no deberían ser fijos, escritos en el código, sino provenientes de un artefacto de servidor, como pueda ser una API, un Web Service, o cualquier otra fuente dinámica de datos. Esta es una cuestión de la que hablaremos más adelante. De momento, para aclarar conceptos, lo usaremos así.

El estado actual de la aplicación te lo puedes descargar en este enlace.

   

Deja un comentario