Comunicar AngularJS con el Servidor

De ChuWiki

Veamos un ejemplo sencillo de cómo comunicar AngujarJS con el servidor, para pedirle datos, modificarlos, acceder a recursos o lo que sea. En este ejemplo simplemente pediremos una lista de datos para presntarla en pantalla. Puedes echar un ojo a Ejemplos Basicos con AngularJS para coger las ideas básicas de AngularJS. Tienes el proyecto completo de este ejemplo en AngularJSService


Módulos de Angular[editar]

Como paso previo, porque lo necesitaremos un poco más adelante, vamos a comentar qué son los módulos de AngularJS. En Ejemplos Basicos con AngularJS vimos que un controlador de AngularJS no es más que una función javascript y simplemente la declarabamos de forma global

function HelloController($scope) {
   $scope.greeting = 'hello';
}

Si nuestra aplicación crece, no es buena idea declarar tanta función global, es mejor repartir los controladores y las demás cosas que necesitemos en módulos separados. AngularJS permite hacer esto usando la función angular.module(). Primero creamos un módulo dándole un nombre, por ejemplo, myApp

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

y ahora tenemos varios métodos posibles en myApp para registrar controladores de AngularJS y otros tipos de elementos que también podemos usar en AngularJS como servicios, directivas, ... En el caso concreto de querer registrar un controlador, deberíamos llamar al método controller() así

myApp.controller('HelloController', function ($scope) {
   $scope.greeting = 'hello';
});

Como primer parámetro se pasa un String con el nombre que queramos dar al controlador, y como segundo parámetro la función controlador en si misma.

La única diferencia en el código HTML correspondiente es que en vez de poner <body data-ng-app> a secas, debemos ahora dar el nombre del módulo <body data-ng-app="myApp"> siendo el nombre el que hemos puesto entre comillas en la llamada a angular.module()

OJO: module() es un poco "raro" en su comportamiento. Si la pasas como segundo parámetro un array (que sirve para pasarle dependencias de otros módulos), aunque sea vacío como en este caso, crea un módulo nuevo, "machacando" el módulo antiguo con el mismo nombre si lo hubiera. Si queres obtener el mismo módulo que ya habías creado anteriormente, no debes pasar segundo parámetro.

// Crea un modulo, por pasar el segundo parametro []
var myApp = angular.module('myApp',[]);
myApp.controller(...); // crea un controller

// Obtiene el mismo modulo, con controller ya definido,
// al no haber pasado ningún array como segundo parámetro
var myApp2 = angular.module('myApp'); 

// Crea de cero el modulo myApp otra vez, eliminado el controller existente
myApp3 = angular.module('myApp',[]); 

$http[editar]

AngularJS nos proporciona un servicio llamado $http con el que podemos hacer llamadas AJAX al servidor, es decir, hacer una llamada para obtener o enviar datos sin necesidad de recargar la página completa. Para tener acceso a este servicio, basta con ponerlo como parámetro en la función javascript que usemos como controlador. AngularJS se encargará de pasárnoslo.

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

myApp.controller('HttpController',function($scope,$http){
   ...
});

El servicio $http se le llama pasando entre paréntesis un objeto javascript con varios atributos. Entre ellos, los mínimos para funcionar son method (get/post/...) y la url a la que llamar, así

$http({method:'GET',url:'users.json'});

users.json sería la url que en nuestro caso es un fichero de nombre user.json alojado en el servidor.

$http admite más parámetros, por ejemplo, se puede poner data para pasar datos por POST o params para pasar datos por GET y muchos más (ver API de $http )

Existen pequeños "atajos" para llamar a este servicio. Por ejemplo, si la llamada va a ser GET, podemos hacer directamente

$http.get('user.json');

La llamada a $http haría una llamada AJAX a la URL indicada y devuelve un objeto con dos métodos interesantes. Uno es success(), al que podemos pasar una función javascript a la que se llamará cuando se tenga una respuesta con éxito, pasando los datos recuperados. La otra es error(), a la que se puede pasar una función a la que se nos llamará si ha habido error. Más o menos esto

var http = $http({method:'GET',url:'users.json'});

http.success(function(data,status,headers,config) {
   // Hacer algo con los resultados en data
});

http.error(function(data,status,headers,config) {
   // Hacer algo con el error en status
});

Suponiendo que todo va a ir bien, nuestro controlador debería guardar los datos recibidos en alguna variable de $scope de forma que sean accesibles desde la plantilla HTML, o bien tratarlos para guardar algún resultado en la variable $scope. Si en nuestro ejemplo users.json es un fichero en el servidor con un array de nombres, el controlador puede ser así

myApp.controller('HttpController',function($scope,$http){
   $http.get('users.json').success(function(data){
      $scope.sameItems=data; // este es el array de nombres recuperado del servidor
   });
});

Hemos concatenado la llamada success() directamente, sin necesidad de guardar previamente $http(...) en una variable. No hemos hecho la llamada a error(), nos fiamos de que todo va a ir bien. En este ejemplo tonto, metemos los datos leídos de user.json directamente en $scope.sameItems

En la parte HTML, nada nuevo, simplemente decir que usamos este controlador y utilizar la variable sameItems que hemos rellenado

<body data-ng-app="myApp">
   <div data-ng-controller="HttpController">
      <p data-ng-repeat="item in sameItems">{{item}}</p>
   </div>
</body>


Servicio[editar]

Estas llamadas a $http pueden quedar un poco feas en nuestro controlador. Una opción es crear un servicio que encapsule estas llamadas a $http y luego hacer que nos pasen el servicio al controlador. Ese servicio tendría métodos un poco más amigables. Para crear un servicio, basta hacer una llamada a service() del módulo angular en el que queramos crearlo, así

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

myApp.service('Users',function($http){
	this.query = function() {
	   return $http({method:'GET',url:'users.json'});
	};
});

Llamamos a myApp.service() para crear el servicio. Pasamos como parámetro la cadena Users, que es el nombre que queremos dar al servicio y como segundo parámetro una función que recibe $http y que será nuestro servicio.

En esta función definimos un método query() (o el nombre que queramos, y más métodos si queremos) por medio de this.query=function(). En esa función hamos la llamada a $http ya a medida con su method, url y lo que necesitemos. La funcion query() podría incluso admitir parámetros que serían los datos/parametros a enviar por POST/GET en la llamada.

Con esto quedaría registrado el servicio Users que sería una clase (función) con un método query() que devuelve una llamada a $http ya hecha.

Nuestro controlador podríamos ahora declararlo así

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

myApp.controller('ServiceController',function($scope,Users){
	Users.query().success(function (data) {
		$scope.items=data;
	});
});

Se pasa como parámetro directamente Users, con el mismo nombre que habíamos puesto al crear el servicio. AngularJS será capaz de esta forma de pasarnos el servicio Users cuando llame al controlador.

Dentro del controlador, podemos hacer lo que queramos, entre otras cosas, llamar a los métodos que tiene Users, como query(). El método query() nos devolvía la llamada hecha a $http, así que debemos llamar a success() y/o error() para tratar los datos recibidos o los errores si se producen.

Igual que antes, somo optimistas y solo tratamos el caso bueno success(), metiendo los datos recibidos en $scope.items

La parte HTML ya no tiene secretos

<div data-ng-controller="ServiceController">
   <p data-ng-repeat="item in items">{{item}}</p>
</div>

Este es un ejemplo muy sencillo. En un caso más real, el servicio Users podría tener varios métodos típicos de acceso y modificación de base de datos, como list(), get(id), delete(id), update(user), insert(user), etc, cada uno haciendo su llamada con $http AJAX al servidor.