¿Banana in a Box? ¿Banana en la Caja? - El Mundo de Angular

¿Banana in a Box? ¿Banana en la Caja?

¿Qué es «Banana In a Box»?, es una terminología de Angular que se refiere a ésta notación [()] que básicamente hace referencia a el famoso two-ways biding, o sea, al mecanismo de actualizar la vista y el modelo al mismo tiempo. Si esto no lo entendés, no te preocupes porque en este post te vamos a aclarar esa y muchas dudas más.

Angular está pensado para actualizar datos en la pantalla dinámicamente, me refiero a que en la pantalla en la que estamos, los datos van cambiando automáticamente sin referescar el browser. Por ejemplo, cuando apretás un botón, al ingresar tu email en un formulario, o si estás en una aplicación de chat, si la otra persona te escribe un mensaje, tu pantalla se actualiza con nuevos datos. Pero ésto no se hace mágicamente, los datos viajan del controlador a la vista (la pantalla que estás viendo) y también se puede dar el caso los datos viajan del controlador a la vista. Y por último, se puede dar el caso de que ambas acciones puedan pasar al mismo tiempo. Estos tres casos se pueden ver gráficamente de la siguiente forma:

 

 

Para cada uno de esos casos, Angular tiene diferentes mecanismos para actualizar la pantalla. Pero antes de explicártelos, vamos a aclarar algo importante: viste que en nuestro modelo del Componente tenemos un Componente con su Template (la Vista) (el archivo HTML)?

Bueno, Angular cuando actualiza datos en pantalla, no trabaja directamente con el HTML sino con el DOM. el DOM o Modelo del Objetos de Documento (de sus siglas en inglés Document Object Model); es la representación que nuestro browser hace del HTML que le pasamos. El browser que elegimos para visualizar nuestra aplicación no muestra directamente el archivo HTML, sino que lo transforma a una representación de un árbol de dependencias, como se muestra en la siguiente imagen:

Por todo ésto, cuando decimos que Angular actualiza la pantalla, lo que queremos decir es que actualiza del DOM. El HTML es sólo el «estado inicial» en el que se va a crear el DOM pero luego, éste puede cambiar dinámicamente, y es esto lo que el usuario termina viendo .

 

Dicho ésto, veamos diferentes mecanismos para decirle a el DOM que se actualice.

 

Interpolación

La interpolación es la forma de mostrar datos del Componente al DOM (esa representación que hace el browser del HTML con forma de objetos). Su notación es en forma de doble brackets {{}} y lo que está dentro de esos brackets es lo que se quiere mostrar en pantalla «procesado».

 

En el ejemplo vemos que hay una variable llamada «título» que tiene el valor «hola mundo», y ésta variable se quiere mostrar en la pantalla. Lo que Angular hace cuando encuentra los doble brackets ({{}}) es procesar el contenido de esa variable y devolverlo en forma de string.

¡Ojo! no sólo hace eso una vez, sino que, si en el medio cambiamos el valor de esa variable «título», el valor se va a actualizar en la pantalla sin que nosotros hagamos nada.

Lo que va dentro de {{ }} se llama Template Expression y es la expresión a evaluar. Ésta expresión produce un valor, que es un string.

Los tipos de Template Expresion válidos pueden ser:

  • {{ 1 + 1}}: la cual devuelve 2
  • {{ miVariable }}: donde miVariable es una variable definida en el Component (como vimos en el ejemplo anterior)
  • {{ miMetodo() }} : donde miMetodo() es un método definido en el Component

También se le puede asignar a un atributo HTML (que espere un string) el valor de una Template Expresion, por ejemplo

<h1 innerText = {{pageTitle}}><h1>   ←  se asigna un valor “ya procesado” a un Tag HTML

De hecho <h1 innerText = {{pageTitle}}><h1> y <h1>{{pageTitle}}</h1> hacen lo mismo

 

Property Binding

Al igual que la Interpolación, Property Binding se usa para pasar datos del Controler al DOM, y los datos van en una única dirección

La sintaxis es la siguiente:

Donde «algo» es también un Template expression. Esta forma es muy común en el mundo de Angular, de hecho para muchos casos es mas común que la Interpolación, entre otras cosas, porque esta forma es mas potente.

Fijaté que la propiedad a modificar se encierra entre «[]», si no lo encerrás de esa forma esa propiedad no se va a modificar, y Angular no le va a dar bola a los que le pases, lo va a tomar como un simple string. Así que no te olvides de encerrar tu propiedad entre «[]».

Esta forma puede aplicarse tanto a un elemento, como a un componente, como a una directiva, o sea, se le pueden pasar datos a cualquiera de esos tres:

  • elemento: <img [src]=”vehicle.imageUrl”>
  • componente: <vehicle-detail [vehicle]=”currenVehicle”></vehicle-detail>
  • directiva: <div [ngClass] = “{selected: isSelected}”> X-Wig</div>

No te olvides de los brackets

Voy a ser repetitivo porque este es un error muy común. Si por ejemplo hacés <img src=”vehicle.imageUrl”> sin los brackets  esto NO TE VA A FUNCIONAR como vos querés, 

Cosas que NO hace Property Binding

  • No podés actualizar valores del elemento a la variable, solo podés setear propiedades de «afuera» para «adentro»
  • No podés llamar a una función (para eso se usa event binding)
  • No podés quedarte «escuchando» que una propiedad actualice tu variable o lo que le pases <img [src]="miVariable"/>, no podés quedarte escuchando a que «algo» actualice miVariable y te avise. Sólo podés setearle el valor a la propiedad «src» (para eso se usa Banana in a Box)

 

¿Cuándo usamos Property Binding y cuando Interpolación?

 

Básicamente, cuando el valor que vas a setear es un string, no hay diferencia entre usar uno y otro, así que podes usar el que más te guste. Te recomiendo usar una forma y mantenerla en todo tu proyecto para facilitar la lectura del mismo.

En cambio, cuando lo que seteás no es un string, entonces sí o sí tenés que usar Property Binding.

 

Event Binding

Ésta es la forma de enviar «algo» del elemento al componente. La sintaxis es (evento)=»template statement». Cualquier acción que haga el usuario (ingresar texto, apretar un botón, scrollear con la ruedita del mouse) es una acción que se puede capturar. Cualquiera de esas acciones puede disparar una acción desde el elemento DOM hacia el Controlador.

 

La forma de capturar cualquiera de esas acciones es quedarse escuchando un evento en particular y en el momento en que ocurran, ejecutar una acción.

La sintaxis es la siguiente:

El target event o evento a capturar es cualquier cosa que queremos capturar del usuario (clicks, tecleo de teclas, movimientos del mouse, etc).

$event y el manejo de eventos

Cuando se declara un Event Binding, Angular le asigna al Evento a capturar un handler (o un manejador). Este manejador tiene como objetivo ejecutar la «template statement» cuando el evento ocurra. Además, lo que hace el handler es recibir un objeto llamado $event (este objeto es creado por Angular) y lo deja disponible para la Template statement por si necesita algún dato del evento.

Por ejemplo, en el Tag HTML «input» que captura datos del teclado del usuario, si uno quisiera capturar qué tecleó el usuario, se podría acceder de la siguiente forma: $event.target.value. El $event cambiará dependiendo del evento que se capture.

 

¿Y cómo creamos nuestro propio Event Binding?

Muy fácil, usando la clase EventEmitter proporcionada por Angular. Esta clase tiene un método llamado emit que envía un mensaje de un controlador a otro. Pero es más fácil verlo con un ejemplo, tenemos un componente dentro de otro y queremos que el componente interno le envié un mensaje al componente externo:

Y lo que queremos hacer es que el Componente Interno le envié un mensaje al Componente Externo. Para el ejemplo, enviaremos ese mensaje ni bien se crea el componente (en el método Oninit() )

Hay dos «etapas», una es la registración del handler del evento, y la otra es el envío del mensaje del Componente Interno hacia el Externo (app)

 

Y la etapa de envío del mensaje, en este ejemplo, se hace en el método ngInit() para que se ejecute justo después de crearse. El ejemplo es muy simple, y el receptor del mensaje, el Componente «app», solo lo imprimirá en pantalla, usando la variable $event que crea el handler del evento. En este caso la variable $event tiene el string que envía el componente «interno»: «HOLA»

Y ahora si, Banana In a Box!

Es la forma de two-way bidning o su forma común «banana in a box«, es la combinación de Property binding con Event binding, o sea, mezclamos [] con () y tenemos [()].

¿Para qué se usa el two-way bindings?, para enviar datos del DOM al Componente y del Componente al DOM, por eso se llama two-way binding

La forma mas común de usarlo es con la Directiva ngModel, por ejemplo, si tenemos ésto en un HTML:

Si el usuario teclea algo, el Controlador será notificado y podrá efectuar una acción con eso. Y por otro lado, si el Controlador modifica la variable nombre (por ejemplo borrando los datos) el DOM se actualizará automáticamente. ¡Y nosotros no vamos a hacer nada!

Ojo, para que funcione la directiva NgModule hay que importar el módulo FormsModule.

Oka todo muy lindo, pero ¿cómo es que ésto funciona?

Bueno, en realidad esta notación es solo syntactic sugar (o sea, un código especial que luego se remplaza por otro y que a nosotros nos hace la vida mas fácil, no teclear toda el código completo)

Volviendo a nuestro ejemplo de la directiva ngModel, si quisiéramos hacer una directiva que se comporte de forma similar (o sea, que pueda actualizar un dato que se le pasa tanto dentro del componente como fuera de éste), haríamos lo siguiente:

 

Volvemos a tener un Componente interno (llamado «entrada») y uno externo «app».

Y lo que queremos hacer es pasarle un valor (un número) al componente «entrada» (desde el componente externo «app») y que, cuando éste lo modifique, se refleje el cambio tanto con el Componente interno como en el externo. El Componente «entrada» modificará el valor con dos botones, uno que incrementará su valor en 1 y uno que decrementará su valor en 1:

 

 

La sintaxis [(entrada)]  se usa cuando el componente tiene un @Input() llamado «entrada» y un @output llamado «entradaChange» (fijáte la palabra Change al final, y los dos mantienen el mismo nombre «entrada»)

De hecho si no usamos la notación «banana in a box» en ngModel en vez de escribir en nuestro código:

<input [(ngModel)]="myVariable">

escribiríamos la forma «larga»:

<input [ngModel]="myVariable" (ngModelChange)="myVariable=$event">

Conclusiones

Restringir el acceso del DOM al Controlador y viceversa, hace nuestro código mucho más legible, más controlado y menos propenso a errores. Cuando decidas si usar () ó [], o ambos, antes tomáte dos segundos para pensar que querés hacer: pasar datos del DOM al Controlador, del Controlador al DOM o ambos. Si tenés eso en mente, ¡tu código será mucho mejor!

About the Author

Mi nombre es Gustavo Hernán Dohara, soy Ingeniero en Sistemas y, sobre todas las cosas, soy un apasionado por la tecnología; tengo más de 10 años de experiencia en diferentes tecnologías y he decidido compartir mis conocimientos y experiencias con cualquiera que desee aprender: alumnos, empleados, freelancers y vos. Al ritmo que cada uno pueda, en el horario en que cada uno pueda y en el lugar que cada uno elija.