Hoy vamos a ver un ejemplo más real del uso de las directivas.

Uno de los momentos en que ves la necesidad del uso de directivas en AngularJS es cuando necesitas modificar algún elemento del DOM. Si lo haces en el controlador, es muy probable que tengas que duplicar código o sacarlo a un fichero externo, con lo que tu aplicación es “menos angular” que antes. Además, estamos dándole al controlador responsabilidades que no deberían ser suyas. Vamos a ver el ejemplo con una ventana modal. El controlador debe decir ahora abre/cierra la ventana modal, pero no le importa si ésta es un alert, un modal de bootstrap, un fancybox o algo hecho por nosotros. De eso se encargará nuestra directiva.

Para el ejemplo generaremos un modal de Bootstrap 3. Nuestra directiva consistirá en una etiqueta modal, que recibirá dos atributos: visible para determinar cuándo se abrirá la ventana modal, y title para establecer el título del modal. El código HTML que introduzcamos dentro de la etiqueta se pasará al cuerpo del modal gracias a la directiva ng-transclude. Para simplificar el ejemplo, este modal no tendrá pie.

Veremos que el código de la plantilla va a ser el que viene en la documentación de bootstrap. En él, el título se pasa al scope, así como el contenido del body, que se incrustará gracias a la directiva ng-transclude.

angular.module('myModal')
  .directive('modal', function () {
    return {
      template: '<div class="modal fade">' + 
          '<div class="modal-dialog">' + 
            '<div class="modal-content">' + 
              '<div class="modal-header">' + 
                '<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>' + 
                '<h4 class="modal-title">{{ title }}</h4>' + 
              '</div>' + 
              '<div class="modal-body" ng-transclude></div>' + 
            '</div>' + 
          '</div>' + 
        '</div>',
      restrict: 'E',
      transclude: true,
      replace:true,
      scope:true,
      link: function postLink(scope, element, attrs) {
        scope.title = attrs.title;

        scope.$watch(attrs.visible, function(value){
          if(value == true)
            $(element).modal('show');
          else
            $(element).modal('hide');
        });

        $(element).on('shown.bs.modal', function(){
          scope.$apply(function(){
            scope.$parent[attrs.visible] = true;
          });
        });

        $(element).on('hidden.bs.modal', function(){
          scope.$apply(function(){
            scope.$parent[attrs.visible] = false;
          });
        });
      }
    };
  });

Vemos que la directiva vigila el atributo visible del ámbito padre. Cuando su valor es verdadero, el modal se muestra. En caso de ser falso se ocultará. También, capturaremos los eventos de modal abierto/modal cerrado para actualizar el valor en el ámbito padre para evitar discrepancias.

En nuestra vista, el modal podría tener la siguiente forma:

<div ng-controller="MainCtrl">
  <button ng-click="toggleModal()" class="btn btn-default">Open modal</button>
    
  <modal title="Login form" visible="showModal">
    <form role="form">
      <div class="form-group">
        <label for="email">Email address</label>
        <input type="email" class="form-control" id="email" placeholder="Enter email" />
      </div>
      <div class="form-group">
        <label for="password">Password</label>
        <input type="password" class="form-control" id="password" placeholder="Password" />
      </div>
      <button type="submit" class="btn btn-default">Submit</button>
    </form>
  </modal>
</div>

La función toggleModal() únicamente cambia el valor de la variable showModal cada vez que es llamada.

En este fiddle tenemos el ejemplo completo y funcionando.

Anuncios