¿Cuánto queda hasta Pilares?

Si señor, esa es la pregunta clave que debería estar haciéndose todo maño. ¿Cuánto queda hasta Pilares?

Para el que no lo sepa, los Pilares o las Fiestas del Pilar es la fiesta por excelencia de Zaragoza. Una fiesta tradicional de mi tierra que básicamente consiste en hacer una ofrenda floral a la Virgen del Pilar. La fiesta se desarrolla entre los días 8 de Octubre y el día de la Hispanidad, aunque días previos ya existen fiestas no oficiales y es posible salir a volcar fresquitos esos días previos.

Si preguntas a diferentes personas, unos te dirán que lo mejor de los Pilares es la ofrenda del día 12, otros te dirán que el pregón pero lo que yo y muchos jóvenes mas esperamos ansiosamente son las noches de Interpeñas, Espacio Z y la Carpa del Ternasco.

Inflarte a comer carne con denominación de origen en la Carpa del Ternasco mientras te bebes una buena Ambar fresquita al ritmo de Los Gandules para después irte hasta el recinto ferial Valdespartera a pillarte el moco con los calimotxos y el garrafón y acabar la noche comiéndote un calamar bravo es ya un rito típico que se repite todos los años.

Por eso, la aplicación de “¿Cuánto queda hasta Pilares?” es tu mejor opción para saber cuántos días quedan hasta que comiencen las fiestas. No necesitas nada más que un teléfono Android y descargar la aplicación desde la market.

Ale, todos a descargar 😉

Zona developers

Como es ya tradicional cada vez que hago un desarrollo, os voy a dejar a continuación el enlace al repositorio para que podáis descargar el código fuente de la aplicación y le echéis un vistazo.

La aplicación de “¿Cuánto queda hasta Pilares?” esta hecha con Ionic Framework, al igual que las otras apps que he hecho, Test de la Amistad, Nivel de Alerta Antiterrorista y Peliculas Kinkis pero a diferencia de las otras, en esta he utilizado un par de cosas nuevas de Angular.

Para el que no lo sepa, el framework de Ionic utiliza por debajo AngularJS y Apache Cordova.

Directivas en Angular

.directive('countdown', [
  'Util', '$interval', function(Util, $interval) {
    return {
      restrict: 'A',
      scope: {
        date: '@'
      },
      link: function(scope, element) {
        var future;
        future = new Date(scope.date);
        $interval(function() {
          var diff;
          diff = Math.floor((future.getTime() - new Date().getTime()) / 1000);
          return element.text(Util.dhms(diff));
        }, 1000);
      }
    };
  }
])

Este es un pedazo de código fuente de la aplicación y se corresponde con una directiva en AngularJS.

Una directiva es un pedazo de código con el que podemos extender nuestro código HTML, estas directivas pueden ser desde un pequeño cacho de código hasta una funcionalidad completa como la anterior. Existen varios tipos de directivas nativas de Angular como ng-app, ng-controller, ng-model, ng-click… aunque ya entraré en detalle en ellas en otra entrada.

Como veis, a esta directiva a la que he llamado countdown le estamos pasando el servicio Util y el objeto $interval. Después en la vista la utilizo tal que así:

<span countdown='' date='{{date}}' class="date"></span>

La otra funcionalidad que he utilizado para este desarrollo y que no había utilizado en otras apps son los servicios.

Servicios en Angular

Algo parecido a los servicios que definimos en Symfony, los servicios de Angular son unos objetos que se rigen por el patrón Singleton y que se inyectan a través del contenedor de Dependencias. La misión de estos servicios es escribir la lógica de negocio que después podremos reutilizar en cualquier otra parte de la aplicación sin tener que depender de la vista.

.service('Util', [
  function() {
    return {
      dhms: function(t) {
        var days, hours, minutes, seconds;
        days = Math.floor(t / 86400);
        t -= days * 86400;
        hours = Math.floor(t / 3600) % 24;
        t -= hours * 3600;
        minutes = Math.floor(t / 60) % 60;
        t -= minutes * 60;
        seconds = t % 60;
        return [days + 'días', hours + 'horas', minutes + 'minutos', seconds + 'segundos'].join(' ');
      }
    };
  }
]);

Este servicio al que he llamado Util y que después se lo paso como argumento a la directiva anterior hace una cosa tan tonta como calcular el tiempo que queda hasta una fecha futura. Se podría decir que es el núcleo de la aplicación “¿Cuánto queda hasta Pilares?”

Obviamente los servicios, las directivas y el propio framework de Angular dan para una entrada completa o varias cosa que no es objeto de este post pero en el que si que quería hacer una pequeña introducción para que se entienda el código fuente completo.

Os dejo el enlace al repo de github y si tenéis cualquier duda podéis hacerlo en los comentarios o través del banner de Twitter de a continuación.

[xyz-ips snippet=”FAQS-GORKAMU-TW-YELLOW”]

Hala, a mamarla!

Anuncios

Consumir un webservice desde Angularjs

Últimamente estoy dando muchas vueltas con AngularJs. El desarrollo tiene que ir encaminado hacia la creación de aplicaciones web que se comporten de igual manera ya sea si la estas viendo desde un móvil, una tableta o un ordenador. El dispositivo debe ser un elemento totalmente independiente de la experiencia de usuario de una web o una aplicación, por eso en los últimos tiempos han salido varios frameworks que nos ayudan a hacer aplicaciones webs colocando una capa de abstracción entre las particularidades de cada plataforma y el usuario.

Pues bien, como ya algunos sabrán, estoy tonteando mucho últimamente con frameworks de desarrollo de aplicaciones para móviles. Hay muchos en el mercado pero yo he decidido empezar por Ionic, que es un framework lanzado por Twitter y basado en Apache Cordova y AngularJs y del que ya hablé en un artículo anterior.

Lo que tiene en común el desarrollo con este tipo de frameworks es que en algún momento si o si vas a necesitar hacer una llamada a un webservice para consumir unos datos, ya sea mediante una api RESTful o mediane SOAP y aquí es dónde viene el problema con el que me he encontrado estos días, cómo consumir un webservice que devuelve un conjunto de datos en JSON desde AngularJS.

En uno de los controladores de mi aplicación AngularJS, tenía una llamada asíncrona que utilizaba JSONP para comunicarse. Esta técnica de comunicación está pensada para suplir las limitaciones de AJAX en las comunicaciones asíncronas, permitiéndonos hacer peticiones a páginas que se encuentren en otros dominios diferentes sin que nos salten errores de CORS.

¿Y qué son las CORS? Pues quiere decir Cross-Origin Resource Sharing, o en la lengua de Cervantes, Sistema de Compartir Recursos entre Dominios Cruzados y es una política de seguridad del W3C que básicamente consiste en la permisividad de poder acceder a recursos de un dominio desde otro dominio diferente. Esta política nos viene genial para las aplicaciones que vamos a empaquetar con Cordova, pero no solo para apps nos ayuda sino que cada vez es más frecuente diseñar páginas/aplicaciones que beban de diferentes fuentes de datos.

El problema que nos encontrarnos al tener que utilizar esta política de seguridad es que si no la configuramos y utilizamos correctamente estamos dejando una puerta abierta para ataques maliciosos. Alguien con muy malas ideas podría acceder a todos los recursos del servidor mediante un servicio expuesto si no hemos definidos bien las CORS. Que puedan acceder a una imagen que tenemos alojada en nuestro servidor pues a lo mejor nos da un poco igual, pero ya no nos dará tan igual si acceden a las cookies de sesión o realizan ataques CSRF en nuestra web/aplicación…

Pues lo dicho, para consumir un webservice desde AngularJs, tendremos que hacer uso en nuestro navegador del objeto $http. Como dice la fuente original:

The $http service is a core Angular service that facilitates communication with the remote HTTP servers via the browser’s XMLHttpRequest object or via JSONP.

 var dataJSONP = $http({
         method: 'JSONP', 
         url: 'https://example.org/webservice'
});

Lo que hay al otro lado de la url es un webservice que devuelve unos datos en formato JSON. Podría ser algo tan sencillo como:

 $data = array( 'date' => '22/11/2015',
        'title' => 'Título para un post chorra',
   );
 echo json_encode($data); 

Definimos un array y lo devolvemos codificándolo mediante la función json_encode, fin. Sin embargo si ejecutamos esto recibiremos un error como el siguiente:

Cross-Origin Resource Sharing
Cross-Origin Resource Sharing

Para solucionarlo, tendremos que añadir lo siguiente a la respuesta de nuesto webservice:

 header('content-type: application/jsonp; charset=utf-8');
 header('Access-Control-Allow-Headers: Content-Type');
 header('Access-Control-Allow-Methods: GET, POST');
 header('Access-Control-Allow-Origin: *');
 echo 'angular.callbacks._0('.json_encode($arr).')'; 
  • content-type: application/jsonp; charset=utf-8: indicamos el tipo de respuesta y su codificación.
  • Access-Control-Allow-Headers: Content-Type: indicamos que cabeceras HTTP usar al hacer la petición.
  • Access-Control-Allow-Methods: GET, POST: métodos permitidos para hacer la petición.
  • Access-Control-Allow-Origin: indicamos los origenes de la petición permitidos.
  • angular.callbacks._0: JSONP requiere encapsulemos la respuesta en una función de callback de Javascript.

Con esto conseguiremos que nuestra aplicación AngularJs pueda consultar datos desde fuentes externas.

He encontrado una respuesta muy buena en Stackoverflow sobre el uso de angular.callbacks en las respuestas JSONP. Si quieres leerla ayudame a que el artículo se difunda compartiéndolo por las redes sociales.

[sociallocker]

JSONP requires you to wrap your JSON response into a Javascript function call. When you do a JSONP the request query string will set a parameter called ‘callback’ that will tell your server how to wrap the JSON response.

So the response should be something like:

callback([
    {"id": "1", "name": "John Doe"},
    {"id": "2", "name": "Lorem ipsum"},
    {"id": "3", "name": "Lorem ipsum"}
]);

Angular will define the callback parameter as angular.callbacks._0, angular.callbacks._1, angular.callbacks._2 … depending on how many requests it is waiting for response, so if you do a single request the response should be:

angular.callbacks._0([
    {"id": "1", "name": "Lorem ipsum"},
    {"id": "2", "name": "Lorem ipsum"},
    {"id": "3", "name": "Lorem ipsum"}
]);

The server should use the callback parameter from the request string to set the response accordingly.

Check your Plunker’s network activity and you will see that the request is setting the callback parameter but the response you are getting is not being wrapped with it.

[/sociallocker]