Tabla de Contenidos

2.5 Directiva ng-disabled

La directiva ng-disabled nos permite habilitar o deshabilitar un elemento de entrada de datos como un <input> un <select> o un <button>.

Lo importante de la directiva es que habilita o deshabilita el elemento en función de cualquier valor del $scope y por lo tanto de nuestro modelo.

En nuestro ejemplo del seguro médico tenemos el campo “Nombre de la alergia”. Este campo sólo tiene sentido que esté habilitado cuando el usuario ha marcado que tiene alguna alergia, así que vamos a hacer que sólo esté habilitado el <input> del nombre de la alergia cuando el valor $scope.seguro.enfermedades.alergia valga true. Es decir que si marcamos que tenemos alergia , entonces se habilitará el nombre de la alergia y si no marcamos que tenemos alergia se deshabilitará el campo del nombre de la alergia.

<!DOCTYPE html>
<html ng-app="app">
  <head>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.min.js"></script>
    <script src="script.js"></script>
  </head>
  <body ng-controller="SeguroController">
    <form>
      <fieldset>
        <legend>Seguro Médico</legend>
          <label for="nif">NIF:</label><input id="nif" name="nif" type="text" ng-model="seguro.nif" /><br>
          <label for="nombre">Nombre:</label><input id="nombre" name="nombre" type="text" ng-model="seguro.nombre" /><br>
          <label for="ape1">1º Apellido:</label><input id="ape1" name="ape1" type="text" ng-model="seguro.ape1" /><br>
          <label for="edad">Edad:</label><input id="edad" name="edad" type="text" ng-model="seguro.edad" /><br>
          <label for="sexo">Sexo:</label><select id="sexo" name="sexo" type="checkbox" ng-model="seguro.sexo" ><option value="">--Elige opcion--</option><option value="H">Hombre</option><option value="M">Mujer</option></select><br>
          <label for="casado">Casado:</label><input id="casado" name="casado" type="checkbox" ng-model="seguro.casado" /><br>
          <label for="numHijos">Nº Hijos:</label><input id="numHijos" name="numHijos" type="text" ng-model="seguro.numHijos" /><br>
          <label for="embarazada">Embarazada:</label><input id="embarazada" name="embarazada" type="checkbox" ng-model="seguro.embarazada" /><br>
          <label for="fechaCreacion">Fecha de creaci&oacute;n:</label><input id="fechaCreacion" name="fechaCreacion" type="text" ng-model="seguro.fechaCreacion" /><br>
      </fieldset>
      <fieldset>
        <legend>Coberturas</legend>
          <label for="oftalmologia">Oftalmologia:</label><input id="oftalmologia" name="oftalmologia" type="checkbox" ng-model="seguro.coberturas.oftalmologia" /><br>
          <label for="dental">Dental:</label><input id="dental" name="dental" type="checkbox" ng-model="seguro.coberturas.dental" /><br>
          <label for="fecundacionInVitro">Fecundacion In Vitro:</label><input id="fecundacionInVitro" name="fecundacionInVitro" type="checkbox" ng-model="seguro.coberturas.fecundacionInVitro" /><br>
      </fieldset>      
      <fieldset>
      <legend>Enfermedades</legend>
          <label for="corazon">Corazon:</label><input id="corazon" name="corazon" type="checkbox" ng-model="seguro.enfermedades.corazon" /><br>
          <label for="estomacal">Estomacal:</label><input id="estomacal" name="estomacal" type="checkbox" ng-model="seguro.enfermedades.estomacal" /><br>
          <label for="rinyones">Ri&ntilde;ones:</label><input id="rinyones" name="rinyones" type="checkbox" ng-model="seguro.enfermedades.rinyones" /><br>
          <label for="alergia">Alergia:</label><input id="alergia" name="alergia" type="checkbox" ng-model="seguro.enfermedades.alergia" /><br>
          <label for="nombreAlergia">Nombre de la alergia:</label><input ng-disabled="seguro.enfermedades.alergia===false" id="nombreAlergia" name="nombreAlergia" type="text" ng-model="seguro.enfermedades.nombreAlergia" /><br>
      </fieldset> 
    </form>
  </body>
</html>

var app=angular.module("app",[]);
 
function SeguroController($scope) {
  $scope.seguro={
    nif:"",
    nombre:"",
    ape1:"",
    edad:undefined,
    sexo:"",
    casado:false,
    numHijos:undefined,
    embarazada:false,
    coberturas: {
      oftalmologia:false,
      dental:false,
      fecundacionInVitro:false
    },
    enfermedades:{
      corazon:false,
      estomacal:false,
      rinyones:false,
      alergia:false,
      nombreAlergia:""
    },
    fechaCreacion:new Date()
  }
}

El fichero script.js no se ha modificado desde el ejemplo anterior.

Expresión de la directiva

Hemos visto en el ejemplo anterior que en ng-disabled se ha usado la expresión seguro.enfermedades.alergia===false. Pero, ¿qué se puede poner realmente como valor del atributo?. La respuesta sencilla es que se puede poner cualquier expresión JavaScript pero se evaluará referida al $scope. La respuesta un poco más complicada es que realmente no es JavaScript sino un subconjunto de él. Es AngularJS el que se encarga de interpretar las expresiones por lo que no podremos usar cualquier expresión de JavaScript sino sólo aquello que haya implementado AngularJS en su interprete de las expresiones.

Alguno de los motivos por el que AngularJS ha creado su propio interprete de expresiones es:

  • Hacer que siempre sean referidas al $scope
  • Evitar problemas de seguridad al no permitir toda la potencia de JavaScript

Ejemplos de expresiones usando nuestro modelo podrían ser:

seguro.edad>15

seguro.nif===null || seguro.nif===""

seguro.nombre==="Carlos"

seguro.sexo==="H" && sexo.embarazada===true

Llamando a Funciones

También podemos llamar a funciones si creamos dicha función en el $scope.

Por ejemplo si definimos la siguiente función:

$scope.disabledNombreAlergia=function() {
  return ($scope.seguro.enfermedades.alergia===false)
}

podremos llamar a la función desde la directiva, quedando de la siguiente forma:

<input ng-disabled="disabledNombreAlergia()" id="nombreAlergia" name="nombreAlergia" type="text" ng-model="seguro.enfermedades.nombreAlergia" />

Ahora desde la directiva llamamos a la función disabledNombreAlergia() pero sólo porque dicha función está definida en el $scope.

Funciones con parámetros

Otra posibilidad es que a la función se le pueden pasar parámetros, tanto literales como valores del $Scope o incluso el resultado de otras funciones. Todo ésto recordad que se evalua referido siempre al $scope.

Un ejemplo un poco rebuscado para ver éso es el siguiente:

$scope.suma=function(a,b) {
   return a+b;
}

$scope.isNegativo=function(c) {
  if (c<0) {
    return true;
  } else {
    return false;
  }
}

Hemos definido la función suma que acepta dos parámetros y la función isNegativo que acepta uno.

<input ng-disabled="isNegativo(suma(seguro.edad,10))" id="nombreAlergia" name="nombreAlergia" type="text" ng-model="seguro.enfermedades.nombreAlergia" />

Ahora vemos cómo desde la directiva se llama a la función isNegativo pasándole como argumento el resultado de llamar a la función suma. A la función suma se le pasan 2 argumentos, uno es el valor del propio $scope correspondiente a seguro.edad y el otro es el valor 10.

Ahora si ponemos una edad negativa se deshabilita el campo nombreAlergia. Obviamente esto no es de ninguna utilidad pero sirve de ejemplo de como llamar a funciones con argumentos.

Una referencia completa de que se puede poner como expresión la tenemos en angular_expressions_cheatsheet.pdf

Reglas de negocio del Interfaz de usuario

Una cosa que se debe remarcar es la posibilidad de crear fácilmente reglas de negocio relativas al funcionamiento del interfaz de usuario.

En nuestro ejemplo hemos definido la siguiente regla de negocio relativa al interfaz de usuario:

“Si el campo “alergia” está desactivado, el campo “nombre de la alergia” debe deshabilitarse”

Antes de AngularJS 1) debíamos comprobar todas las veces que cambiábamos el valor de “alergia” yacordarnos de ejecutar la regla que deshabilitaba el campo “nombre de la alergia”. Ésto resultaba un problema ya que era fácil que olvidáramos ejecutar dicha reglas.

Aunque pueda parecer sencillo, el tema se complicaba mucho al haber muchas reglas y muchos valores que disparaban esa reglas. Estas reglas suelen ser del tipo:

SI Se modifica el valor X1 o X2,…Xn ENTONCES Hacer la acción sobre Y

El problema era que las reglas se debían programar tras el código que modifica X1 y tras el código que modifica X2 y … tras el código que modifica Xn y si cada uno de ellos se modifica en varios sitios, hacerlo en cada uno de ellos, cuando lo realmente cómodo sería poner la regla una única vez y que se llamara automáticamente cuando hiciera falta.

Gracia a AngularJS ahora sólo es necesario poner la regla una única vez y ya se llamará cada vez que se modifique alguno de los valores de los que depende. Éso simplifica significativamente nuestro código ya que ahora sólo ponemos la regla una única vez independientemente de cuántos valores dependa y de en cuántos sitios se modifique cada uno de esos valores.

Ejemplo

Referencias

1) O Ember o Knockout, etc.