Tabla de Contenidos

5.6 Creando filtros

En este tema vamos a ver cómo crear filtros personalizados. Crear un nuevo filtro es casi tan sencillo como crear una función a la que le pasamos un valor y ésta retorna el nuevo valor con el filtro aplicado.

La forma de crear un filtro es llamando al método filter de un módulo. Este método acepta una función de factoría,siendo la función de factoría la que retornará nuestra función de filtro.

Veamos un ejemplo muy simple. Vamos a volver a crear el filtro de pasar a mayúsculas.

Lo primero es crear la función que hace de filtro

function mayusculasFilter(valor) {
  if (typeof (valor)==="string") {
    return valor.toUpperCase();
  } else {
    return valor;
  }
}

Vemos que es muy sencilla la función , aunque lo único que debemos recordar es que nos pueden pasar el filtro con cualquier valor , así que debemos comprobar el tipo de lo que nos han pasado.

Ahora toca definir la función de factoría que retorna la función del filtro.

function mayusculasFilterFactory() {
  return mayusculasFilter;
}

Al igual que con los servicio factory, el que sea una función de factoría nos permite inicializaciones complejas del filtro.

Por último nos queda definir el filtro en AngularJS

app.filter("mayusculas",mayusculasFilterFactory);
Ahora simplemente le decimos a AngularJs el nombre real del filtro y el nombre de la función de factoría. Vemos que son 3 pasos y puede parece un poco largo, pero así queda muy clara la función de cada uno.

El código completo del filtro lo podemos ver ahora:

function mayusculasFilter(valor) {
  if (typeof (valor)==="string") {
    return valor.toUpperCase();
  } else {
    return valor;
  }
}


function mayusculasFilterFactory() {
  return mayusculasFilter;
}

app.filter("mayusculas",mayusculasFilterFactory);

Como la función de factoría es tan simple, se podría poner como una función anónima al definir el filtro:

function mayusculasFilter(valor) {
  if (typeof (valor)==="string") {
    return valor.toUpperCase();
  } else {
    return valor;
  }
}

app.filter("mayusculas",function () {
  return mayusculasFilter;
});

Por último, en muchos ejemplos de creación de filtros se hace todo con funciones anónimas:

app.filter("mayusculas",function () {
  return function (valor) {
    if (typeof (valor)==="string") {
      return valor.toUpperCase();
    } else {
      return valor;
    }
  };
});

Aunque queda más compacto y durante mucho tiempo yo mismo lo he hecho así. Al final mi recomendación es crear la función de filtro por un lado y dejar la factoría como función anónima.

El motivo es que, con tanta función anónima al final te lías un poco. Así que, donde está lo importante es la función del filtro, por éso la hacemos de forma independiente. Y como la función de factoría es muy simple, la ponemos como función anónima.

La regla anterior tiene su excepción , si la función de filtro necesita de algún servicio inyectado necesitaremos definirla dentro de la función de factoría ya que necesitaremos tener acceso al servicio y no lo podemos pasar como argumento ya que de esa forma romperíamos el contrato del filtro porque no se le pueden pasar más parámetros.Aunque, como siempre en informática , con algún truco sí que se podría dejar fuera la función.

Inyección de dependencias

Un motivo para tener una función factoría que retorna el propio filtro es que podemos inyectar el servicio en la función de factoría.

Vamos a modificar el ejemplo para que se genere un log al crear el filtro usando el servicio de $log de AngularJS.

function mayusculasFilter(valor) {
  if (typeof (valor)==="string") {
    return valor.toUpperCase();
  } else {
    return valor;
  }
}

app.filter("mayusculas",['$log',function ($log) {
  $log.log("Creando el filtro");
  return mayusculasFilter;
}]);

filtros para arrays

Ahora vamos a hacer que a un array se le transformen sus elementos a mayúsculas, siempre que el elemento sea un String.

Podríamos pensar que tenemos que hacer un nuevo filtro para soportar array pero nada impide que usemos el mismo filtro que antes para tratar también los Arrays. Lo que vamos a hacer es comprobar si el valor que nos pasan es un Array y en ese caso recorreremos cada elemento y si es un String lo transformaremos en mayúsculas.

El código del filtro queda ahora así:

function mayusculasFilter(valor,start) {
  
  if (typeof (valor)==="string") {
    return valor.toUpperCase();
  } else if (angular.isArray(valor)) {
    var newValue=[];
    
    for(var i=0;i<valor.length;i++) {
      if (typeof (valor[i])==="string") {
        newValue.push(valor[i].toUpperCase());
      } else {
        newValue.push(valor[i]);
      }
    }
    
    return newValue;
  } else {
    return valor;
  }
}

Angular no distingue entre filtro de escalares y filtro para arrays. Es un problema de nuestro filtro el comprobar el tipo del valor a filtrar y actuar adecuadamente en cada caso.

Como hemos visto en nuestro ejemplo, se usa el mismo filtro tanto para un escalar como para un array.

Parámetros

Ahora veamos cómo incluir parámetros en nuestro filtro. Ya hemos visto el uso de parámetros en filtros como date o filter.

Incluir un parámetro en el filtro es tan sencillo como incluir dicho parámetro en nuestra función del filtro y hacer uso de él.

Vamos ahora a modificar el ejemplo añadiendo un parámetro de tipo numérico llamado length que indicará que sólo queremos que los primeros length caracteres se transformen a mayúsculas.

function mayusculasFilter(valor,length) {
  
  if (typeof (valor)==="string") {
    
    if (angular.isNumber(length) && length>=0) {
      return valor.substr(0,length).toUpperCase()+valor.substr(length);
    } else {
      return valor.toUpperCase();
    }
    
    
  } else if (angular.isArray(valor)) {
    var newValue=[];
    
    for(var i=0;i<valor.length;i++) {
      if (typeof (valor[i])==="string") {
        if (angular.isNumber(length) && length>=0) {
          newValue.push(valor[i].substr(0,length).toUpperCase()+valor[i].substr(length));
        } else {
          newValue.push(valor[i].toUpperCase());
        }
      } else {
        newValue.push(valor[i]);
      }
    }
    
    return newValue;
  } else {
    return valor;
  }
}

Ahora si aplicamos el filtro así:

<div>{{ "hola mundo" | mayusculas:3 }}</div>

se generará lo siguiente:

<div>HOLa mundo</div>

Fíjate que el parámetro del filtro siempre será el segundo parámetro de la función ya que el primer parámetro de la función es el propio valor a filtrar.

Ejemplo

En el ejemplo hacemos uso del filtro mayusculas que hemos creado mediante el siguiente HTML

  {{ valorEscalar | mayusculas:3}}
  <br>
  <br>
  <div ng-repeat="valor in valorArray | mayusculas:3">
    {{valor}}
  </div>

Siendo el valor del $scope el siguiente:

$scope.valorEscalar="hola mundo";
$scope.valorArray=['lunes','martes',3.1416,'miercoles','jueves'];

Vemos cómo el tercer elemento del array es un número.En ese caso, tal y como lo hemos programado, se retorna el propio número.

Referencias

1) No el nombre del filtro que definiremos posteriormente