Directivas ngInit, ngRepeat y ngClick

  • Por
Ejemplo AngularJS en el que vamos a poder explorar el desarrollo únicamente con código HTML y aprender las directivas ngInit, ngRepeat y ngClick.

Seguimos aprendiendo AngularJS y en este artículo vamos a avanzar un poquito más y hacer un ejemplo práctico en el que no tenemos todavía necesidad de escribir código Javascript. Es interesante entretenerse con estos ejemplos, pues resultan muy sencillos y nos ayudan a mantener una progresión muy asequible en el principio de nuestro aprendizaje. También es apropieado para observar bien lo que a veces llamamos la "magia de AngularJS". Veremos que, con muy poco o nada de código, se pueden hacer cosas medianamente importantes, al menos para los desarrolladores que sabemos lo laborioso que sería montar esto por nuestra cuenta con Javascript a secas.

Nos servirá también para aprender nuevas directivas, de las más sencillas y usadas habitualmente, como son ngInit y ngClick o ngRepeat. Así que si os parece comenzamos con un poco de teoría para explicar estas directivas.

Directiva ngInit

Esta directiva nos sirve para inicializar datos en nuestra aplicación, por medio de expresiones que se evaluarán en el contexto actual donde hayan sido definidas. Dicho de otra manera, nos permite cargar cosas en nuestro modelo, al inicializarse la aplicación.

Así de manera general podemos crear variables en el "scope", inicializarlas con valores, etc. para que en el momento que las vayas a necesitar estén cargadas con los datos que necesitas.

Nota: Realmente no hemos explicado todavía en el Manual de AngularJS, al menos suficientemente bien, qué es eso del "scope" y ya estamos abusando en el número de veces que usamos el término. Por ahora hemos comentado que el scope mantiene los datos de tu aplicación, lo que en el paradigma de la programación MVC se llama "modelo". Así pues, si digo que creo variables en el scope lo puedes entender como la definición de un dato de tu aplicación, que se guarda en el denominado "modelo" del MVC. Para los que tienen dudas sobre qué es el MVC os recomendamos leer el artículo Qué es MVC.

<div ng-app ng-init="miArrayDatos = [];">

Con esto consigues que tu aplicación inicialice en el scope un dato llamado miArrayDatos como un array vacío. Pero no le prestes demasiada atención al hecho de haber colocado la directiva ngInit dentro de la misma etiqueta que inicializa la aplicación, pues podría ir en cualquier otra etiqueta de tu HTML. Realmente, colocarla en esa división marcada con ngApp es considerado una mala práctica. Ten en cuenta lo siguiente cuando trabajes con ngInit:

El único caso apropiado donde se debería de usar ngInit es en enlace de propiedades especiales de ngRepeat. Si lo que quieres es inicializar datos en tu modelo para toda la aplicación, el lugar apropiado sería en el controlador. Enseguida vemos un ejemplo de uso apropiado, cuando conozcamos la directiva ngRepeat.

Nota: Aclaramos ya que en el ejemplo final de este artículo vamos a usar ngInit para asociar datos al scope, e inicializarlos. Sin embargo, debemos de ser conscientes que para ello deberíamos usar un controlador. Como no hemos llegado a los controladores todavía, y queremos limitarnos solo a trabajar con el HTML, nos sirve como método alternativo.

Directiva ngRepeat

Esta directiva te sirve para implementar una repetición (un bucle). Es usada para repetir un grupo de etiquetas una serie de veces. Al implementar la directiva en tu HTML tienes que decirle sobre qué estructura se va a iterar. ngRepeat se usa de manera muy habitual y se verá con detenimiento en decenas de ejemplos. De momento puedes quedarte que es como un recorrido for-each en el que se itera sobre cada uno de los elementos de una colección.

La etiqueta donde has colocado el atributo ng-repeat y todo el grupo de etiquetas anidadas dentro de ésta, funciona como si fuera una plantilla. Al procesarse el compilador HTML de AngularJS el código HTML de esa plantilla se repite para cada elemento de la colección que se está iterando. Dentro de esa plantilla tienes un contexto particular, que es definido en la declaración de la directiva, que equivale al elemento actual en el bucle. Se ve mejor con un ejemplo.

<p ng-repeat="elemento in miColeccion">
Estás en: <span>{{elemento}}</span>
</p>

El dato miColeccion sería un dato de tu modelo, habitualmente un array sobre el que puedas iterar, una vez por cada elemento. Pero también podría ser un objeto y en ese caso la iteración se realizaría en cada una de sus propiedades.

En lo relativo al contexto própio del bucle te puedes fijar que dentro de la iteración podemos acceder al dato "elemento", que contiene como valor, en cada repetición, el elemento actual de la colección sobre el que se está iterando.

Esta directiva es bastante sofisticada y explicar cada una de sus posibilidades nos llevaría algo de tiempo. Sin embargo, vamos a mostrar cómo podemos trabajar con ngRepeat en conjunto con ngInit, para completar la explicación de punto anterior.

<p ng-repeat="elemento in miColeccion" ng-init="paso=$index;">
Elemento con id {{paso}}: <span>{{elemento}}</span>
</p>

La directiva ngRepeat maneja una serie de propiedades especiales que puedes inicializar para el contexto propio de cada repetición. Para inicializarlas usamos la directiva ngInit indicando los nombres de las variables donde vamos a guardar esas propiedades. En este caso estamos indicando que dentro de la repetición vamos a mantener una variable "paso" que tendrá el valor de $index, que equivale al número índice de cada repetición. Osea, en la primera iteración paso valdrá cero, luego valdrá uno y así todo seguido. Igual que tienes $index, Angular te proporciona otras propiedades útiles como $first (que valdrá true en caso que sea la primera iteración) o $last (true solo para la última iteración)

Nota: El que acabas de ver sería el único uso de ngInit considerado como buena práctica, en conjunto con ngRepeat para inicializar sus propiedades.

Dejamos de momento ngRepeat, aunque vuelvo a señalar que hay otra serie de cosas interesantes en la directiva para las repeticiones, como filtros, órdenes, etc.

Directiva ngClick

Terminamos nuestra dosis de teoría con una explicación de la directiva ngClick. Como podrás imaginarte es utilizada para especificar un evento click. En ella pondremos el código (mejor dicho la expresión) que se debe ejecutar cuando se produzca un clic sobre el elemento donde se ha colocado la directiva.

Típicamente al implementar un clic invocarás una función manejadora de evento, que escribirás de manera separada al código HTML.

<input type="button" value="Haz Clic" ng-click="procesarClic()">

Esa función procesarClic() la escribirás en el controlador, factoría, etc. Sería el modo aconsejado de proceder, aunque también podrías escribir expresiones simples, con un subconjunto del código que podrías escribir con el propio Javascript. Incluso cabe la posibilidad de escribir varias expresiones si las separas por punto y coma.

<input type="button" value="haz clic" ng-click="numero=2; otraCosa=dato " />

No difiere mucho a como se expresan los eventos clic en HTML mediante el atributo onclick, la diferencia aquí es que dentro de tus expresiones podrás acceder a los datos que tengas en tu modelo.

Nota: Aunque técnicamente pueda escribir expresiones directamente en el código HTML, en el valor del atributo ng-click, tienes que evaluar con cuidado qué tipo de código realizas porque dentro de la filosofía de AngularJS y la del MVC en general, no puedes escribir en tu HTML código que sirva para implementar la lógica de tu aplicación. (El código que necesitas para hacer las funcionalidades de tu aplicación no lo colocas en la vista, lo colocas en el controlador).

Ejemplo de uso de estas directivas en AngularJS

Ahora que hemos conocido las directivas, nos falta ponerlo todo junto para hacer un pequeño ejercicio básico con AngularJS.

Nota: Como advertimos al principio del artículo, solo voy a usar código HTML para este ejercicio. Por ese motivo hay un par de cosas que no serían consideradas buenas prácticas, como el uso de ngInit para inicializar el array "pensamientos" y el hecho de haber colocado dentro de un ng-click expresiones que sería mejor haber escrito en una función de nuestro controlador.

En esta aplicación tenemos un campo de texto para escribir cualquier cosa, un "pensamiento". Al pulsar sobre el botón se agregará dentro de un array llamado "pensamientos" lo que se haya escrito en el campo de texto. Además encuentras un bucle definido con ng-repeat que itera sobre el array de "pensamientos" mostrando todos los que se hayan agregado.

<div ng-app ng-init="pensamientos = [];">
    <h1>Altavoz AngularJS</h1>
    <p>
        ¿Qué hay de nuevo?
        <br />
        <input type="text" ng-model="nuevoPensamiento" /> 
        <input type="button" value="Agregar" ng-click="pensamientos.push(nuevoPensamiento); nuevoPensamiento = '';" />
    </p>
    <h2>Pensamientos que has tenido</h2>
    <p ng-repeat="pensamiento in pensamientos" ng-init="paso = $index">
        Pensaste esto: {{pensamiento}} (Iteración con índice {{paso}})
    </p>
</div>

El dato del array "pensamientos" lo generas en el scope con el ng-init de la primera etiqueta. En el campo de texto tenemos la directiva ng-model para indicarle que lo que se escriba formará parte de nuestro modelo y se almacenará en la variable nuevoPensamiento. Como ves, en el ng-click se hace un push de ese nuevo pensamiento dentro del array de pensamientos. En ng-repeat se itera sobre la colección de pensamientos, escribiéndolos todos por pantalla, junto con el índice de ese pensamiento actual en el array "pensamientos".

Puedes ponerlo en marcha y jugar un poco con la aplicación haciendo tus cambios para calmar tu curiosidad o responder posibles dudas.

Autor

Miguel Angel Alvarez

Miguel es fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Comenzó en el mundo del desarrollo web en el año 1997, transformando su hobby en su trabajo.

Compartir

Comentarios

Sergio

16/9/2014
Duda
Muy bien explicado,por cierto una pregunta,para personalizar el orden de salida de los pensamientos a orden ascendente tengo entendido que se debe usar un filtro, pero luego con la directiva ngClick ser´ia posible cambiar ese filtro para que lo ordenara de forma descendente?.

Marcos

19/9/2014
orden con ngRepeat
Efectivamente, se puede ordenar perfectamente, ascendente, descendente, esto se consigue con filtros, escritos con texto en la propia directiva. Lo puedes ver en uno de los vídeos de Alberto Basalo.
https://www.youtube.com/watch?v=diaCDrTQN5M&list=PLIcuwIrm4rKdPA5DOzbjSKjJXECfzumZf&index=11

gamaliel

31/1/2015
que pasas
si repites un pensamiento igual que el otro ya no pone nada y ademas ya se queda trabado

David

21/3/2015
Error al repetir pensamiento
Por qué no se puede repetir "pensaminto" ??

Erick Blangino

11/6/2015
repetir pensamiento
Buenas, porque da el siguiente error cuando repetimos el valor en el cuadro de pensamiento?

Error: [ngRepeat:dupes] http://errors.angularjs.org/1.2.23/ngRepeat/dupes?p0=pensamiento%20in%20pensamientos&p1=string%3A123
at Error (native)
at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js:6:450
at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js:200:86
at Object.fn (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js:108:354)
at k.$get.k.$digest (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js:109:403)
at k.$get.k.$apply (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js:112:398)
at HTMLInputElement.<anonymous> (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js:194:147)
at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js:31:225
at r (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js:7:290)
at HTMLInputElement.c (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js:31:207)

Daniel P

17/6/2015
Elementos repetidos
EL error Ocurre si hay claves duplicadas en una expresión ngRepeat. Duplicados de las llaves están prohibidos porque angularjs utiliza las claves para asociar DOM nodos con los objetos.
Por defecto, las colecciones están codificados por referencia que es deseable para los modelos más comunes, pero puede ser problemático para los tipos primitivos que están internadas (referencias sobre acciones).
Para solucionar este error agrega al codigo de ng-repeat "track by $index".
La etiqueta <p> quedaria así:
<p ng-repeat="pensamiento in pensamientos track by $index" >

Saludos

Pablo

15/12/2017
Sobre angular
Hay cosas que me encantan de angular como el hecho de solo con una directiva hacer que actualice una vista en tiempo real, pero por otro lado me disgusta directivas como ng-click parecidas al onclick tradicional.

Me parece un retroceso en programación, es mejor usar manejadores de eventos por fuera del html, más limpio el código y fácil de entender al momento de modificaciones. Ir de un archivo al otro todo el tiempo para ver que se está haciendo ralentiza el trabajo.

Ustedes como lo ven ??