Blog

Directiva en AngularJS para seleccionar listado de checkbox Blog

Tags: Angular JS
Una de las funcionalidades básicas en un sistema de administración de contenidos es poder seleccionar varios elementos de un listado (ej: artículos, productos, posts) mediante un checkbox principal.

jQuery de toda la vida

Hacer esto con jQuery es bastante sencillo y sería algo así:

$('input[name=messageId]').each(function () {
    this.checked = !this.checked;
});

Primero seleccionamos el input por nombre e iteramos la lista, cambiando la propiedad checked de cada checkbox. Claro está que ese código debe de estar en un listener del checkbox principal. Algo bastante sencillo y muy común en nuestras aplicaciones web.

The Angular Way

Ahora bien, cuando estamos trabajando con angular tenemos que ver las cosas diferentes, en lugar de manipular el DOM debemos manipular información, a esto es lo que se le denomina The Angular Way, la verdad es que cuando me inicié con angular esto no lo tenía muy claro.

Por todos lados te dicen que no manipules el DOM en tus controladores, que no uses jQuery o selectores ni cualquier otro tipo de manipulación en los controllers. Esto se considera una mala práctica, pero bueno, la verdad es que es complicado cambiar la manera de ver las cosas si estas acostumbrado a utilizar jQuery. De hecho para ser sincero el snippet de código anterior lo saque de la versión que tengo en producción de este mismo sitio (Planeo cambiarlo en una futura revisión).

Angular nos proporciona una manera diferente de hacer las cosas, en este ejemplo voy a utilizar una directiva para poder hacer exactamente lo mismo, pero al hacer una directiva podré reutilizarla en cualquier lugar que lo requiera, y la verdad es que para administrar este sitio tengo varios listados (ej: Cursos, tutoriales, posts, categorías, etc...) y la idea es poder hacer una directiva que me permita seleccionar elementos en todos esos views.

No vamos a manipular el DOM en nuestro controller, sino que manipularemos información, y esta información se verá reflejada en nuestros views, esto es el famoso Angular Way en resumidas palabras.

Primero creamos nuestra aplicación en angular:

var MyApp = angular.module('MyApp',[]);

Ahora vamos a definir el view, con la directiva que estamos creando:

<div ng-app="MyApp">

    
<table ng-controller="ArticlesController">
    <thead>
        <tr>
            <th>
                <input toggle-checkbox toggle-checkbox-list="articles" toggle-checkbox-property="selected" type="checkbox" />
            </th>
            <th>Title</th>
        </tr>
    </thead>
    <tbody>
        <tr ng-repeat="article in articles">
            <td><input type="checkbox" ng-model="article.selected"/></td>
            <td>{{article.title}}</td>
        </tr>
    </tbody>
</table>
    
</div>

La directiva que vamos a crear se llamará toggle-checkbox, en nuestro view definimos dos propiedades importantes.

  1. toggle-checkbox-list, aquí vamos a definir el nombre de la propiedad en nuestro controller que contiene el arreglo de objetos que queremos seleccionar mediante el checkbox.
  2. toggle-checkbox-property, en este atributo definimos la propiedad que decidirá si el checkbox estará seleccionado o no, será una propiedad booleana dentro de cada objeto en la lista anterior.

Por último iteramos la lista articles, definimos un checkbox con el modelo article.selected, este modelo tiene que ser el mismo que definimos en toggle-checkbox-property, ya que desde la directiva le cambiaremos el valor a esta propiedad para habilitar o deshabilitar el checkbox. También estamos imprimiendo el título de cada artículo.

El código JS de la directiva es el siguiente.

MyApp.directive("toggleCheckbox", function() {
    return {
        restrict : "A",
        require: '?ngModel',
        link : function(scope, element, attrs, modelCtrl) {
            var list = scope.$eval(attrs.toggleCheckboxList),
                property = attrs.toggleCheckboxProperty;

            element.bind('change', function() {
                scope.$apply(function () {
                    var isChecked = element.prop('checked');
                    
                    // Set each child's selected property to the checkbox's checked property
                    angular.forEach(list, function(child) {
                        child[property] = isChecked;
                    });
                });
            });
        }
    };
});

La función link se ejecuta por cada vez que definamos esta directiva en nuestro template y se ejecuta una sola vez cuando se crea. 

Primero tomamos el atributo toggleCheckboxList y con el scope.$eval estamos evaluando el string y tomando de la variable del arreglo que tenemos en el controller.

El atributo toggleCheckboxProperty será el que pondremos en true o false para que el checkbox se seleccione dependiendo del valor que le asignemos.

Por último agregamos un listener al evento change del elemento, que en este caso es el checkbox principal, en este evento usamos el $apply para leer la propiedad checked del checkbox principal e iterar la lista de objetos asignando a cada objeto el nuevo valor en la propiedad definida (en este caso selected).

Ya que la propiedad selected esta asignada, para cada checkbox del listado en el view, entonces automáticamente cada checkbox se seleccionará cuando seleccionemos el checkbox principal.

Por último necesitamos crear el controller con la información para poder probar esta directiva.

MyApp.controller('ArticlesController', [
    '$scope',

    function($scope) {
        $scope.articles = [
            {id:1,title:'How to create a directive'},
            {id:2,title:'What is a Controller?'},
            {id:3,title:'JavaScript closures'},
            {id:4,title:'How to Geek'},
            {id:5,title:'Writing your first app'},
            {id:6,title:'Introduction to AngularJS'}
        ];
        
        
    }
]);

Aquí solamente definimos el arreglo articles y creamos los objetos a desplegar en el view, nota que la propiedad selected no está definida en ninguno de los objetos, pero la directiva se encargará de hacerlo.

Puedes ver el ejemplo de este tutorial en acción y probarlo por ti mismo.

Conclusión

Con esta sencilla directiva se puede observar el Angular Way, pareciera que con jQuery es más sencillo, pero la realidad es que eso no es muy reutilizable, hacer una directiva que puedas usar donde quieras con el simple hecho de cambiar los parámetros es mucho mejor, además no estamos manipulando el DOM en el controller y haciendo una buena separación de nuestro código.

Cuando llegamos a angular esto nos puede parecer complicado, pero espero que este tutorial pueda servirte para poder entender el Angular Way, si tienes alguna duda puedes usar el foro.

3Comentarios

  • Avatar-1 Maira 27/03/2015

    Muito bom seu tutorial! Me ajudou nos meus estudos:D

  • Avatar-11 Ronald 23/05/2018

    Hola buenos días espero puedas ayudarme, tengo un problema con el codigo exactamente en la linea var list = scope.$eval(attrs.toggleCheckboxList),. cuando imprimo en consola la variable list me imprime "undefined" Cualquier ayuda es bienvenida

    Instructor del curso

    Crysfel3

    Autor: Crysfel Villa

    Soy un geek que disfruta crear y compartir cosas en internet! Sígueme en twitter @crysfel