====== 3.10 Ejemplo servicio ======
En este tema retornamos el ejemplo del seguro médico para añadir un ''provider''.
Al ver el servicio ''$http'' en el tema [[unidades:03_servicios:02_http]] vimos que no debía hacerse una llamada a ''$http'' en el propio controlador . Ahora vamos a crear un ''provider'' que encapsule la llamada a ''$http''.
El servicio será un objeto llamado ''remoteResource'' que nos permitirá acceder al servidor para acceder a los datos. Como aún no hemos visto la parte de servidor ni la parte de REST ni las promesas , sólo vamos a hacer un único método sencillo que nos permita leer los datos. Más adelante en el curso seguiremos mejorando este servicio. Por lo tanto aun será un poco chapuza el ejemplo.
===== La configuración =====
Nuestro servicio va a ser un ''provider'' ya que tenemos que indicar la url a partir de la que debemos bajar los datos. Como hemos hecho en el tema anterior éso lo pondremos en una constante de nuestra aplicación.
app.constant("baseUrl",".");
La constante vale el valor "." porque los datos a bajar están en la misma ruta que nuestra página web.
El provider tendrá un único método para configurarlo llamado ''setBaseUrl'' por lo que tendremos el siguiente bloque config:
app.config(['baseUrl','remoteResourceProvider',function(baseUrl,remoteResourceProvider){
remoteResourceProvider.setBaseUrl(baseUrl);
}]);
Fíjate cómo el nombre del provider es "remoteResourceProvider", es decir que acaba en "Provider" aunque nuestro servicio sólo se llama "remoteResource".
===== El provider =====
Ahora vamos a definir el provider
function RemoteResourceProvider() {
var _baseUrl;
this.setBaseUrl=function(baseUrl) {
_baseUrl=baseUrl;
}
this.$get=['$http',function($http) {
return new RemoteResource($http,_baseUrl);
}];
}
app.provider("remoteResource",RemoteResourceProvider);
* Línea 1: Definimos el constructor de la clase ''RemoteResourceProvider''
* Línea 3: Un método público llamado ''setBaseUrl'' para configurar el servicio. Éste es el método que hemos llamado desde el bloque config.
* Línea 6: El método ''$get'', que es el factory-provider con la función de factoría a la que se le inyecta el servicio de ''$http''.
* Línea 7: Ahora la función de factoría crea el nuevo objeto de la clase ''RemoteResource'', siendo esta clase realmente la que hace todo el trabajo y la que se usará desde el controlador. En el constructor además de pasarle el ''$http'' que necesita, también se le pasa la URL Base para configurar el servicio.
* Línea 11: Finalmente se registra el ''provider''.
===== La clase del propio servicio =====
Por último nos queda definir la propia clase que implementa el servicio que hemos llamado ''RemoteResource''.
Esta clase tiene un único método llamado ''get''. Este método acepta como parámetros dos funciones de callback . La primera función se llamará si se han podido obtener los datos y tiene como parámetro los datos obtenidos. La segunda función se llamará si ha habido algún error y tiene como parámetros los datos obtenidos y el estado HTTP.
function RemoteResource($http,baseUrl) {
this.get=function(fnOK,fnError) {
$http({
method: 'GET',
url: baseUrl+'/datos.json'
}).success(function(data, status, headers, config) {
fnOK(data);
}).error(function(data, status, headers, config) {
fnError(data,status);
});
}
}
* Línea 1: Al constructor de la clase le pasamos el servicio ''$http'' y la URL Base desde la que pedir los datos.
* Línea 2: Es el único método público llamado ''get'' y como ya hemos dicho acepta dos funciones como parámetros.
* Línea 5: La URL que se usará se calcula en función del parámetro ''baseUrl''.
* Línea 7: Si todo va bien y se han podido obtener los datos, se llama a la función de callback llamada ''fnOK'' pasando como argumento los datos obtenidos.
* Línea 9: Si algo ha fallado, se llama a la función de callback llamada ''fnError'' pasando como argumentos los datos obtenidos y el estado HTTP.
===== El controlador =====
Por último nos queda ver cómo queda el controlador al usar el nuevo servicio.
app.controller("SeguroController",['$scope','remoteResource',function($scope,remoteResource) {
$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()
}
remoteResource.get(function(seguro) {
$scope.seguro=seguro;
},function(data,status) {
alert("Ha fallado la petición. Estado HTTP:"+status);
});
}]);
* Línea 1: Ahora en el controlador en vez de inyectar el servicio ''$http'' se inyecta el servicio ''remoteResource''.
* Linea 26: Se llama al método ''get'' del servicio ''remoteResource'' y se le pasa como argumentos las 2 funciones.
* Línea 27: Si todo va bien , se pasan los datos a la propiedad ''seguro'' del ''$scope''.
* Línea 28: Es la segunda función anónima que se llama si hay algún error.
* Línea 29: Si falla algo se ejecuta el ''alert''.
===== Ejemplo =====
El código completo del ejemplo es el siguiente:
var app = angular.module("app", []);
function RemoteResource($http,baseUrl) {
this.get=function(fnOK,fnError) {
$http({
method: 'GET',
url: baseUrl+'/datos.json'
}).success(function(data, status, headers, config) {
fnOK(data);
}).error(function(data, status, headers, config) {
fnError(data,status);
});
}
}
function RemoteResourceProvider() {
var _baseUrl;
this.setBaseUrl=function(baseUrl) {
_baseUrl=baseUrl;
}
this.$get=['$http',function($http) {
return new RemoteResource($http,_baseUrl);
}];
}
app.provider("remoteResource",RemoteResourceProvider);
app.constant("baseUrl", ".");
app.config(['baseUrl', 'remoteResourceProvider',function(baseUrl, remoteResourceProvider) {
remoteResourceProvider.setBaseUrl(baseUrl);
}]);
app.controller("SeguroController", ['$scope', 'remoteResource',function($scope, remoteResource) {
$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()
}
remoteResource.get(function(seguro) {
$scope.seguro = seguro;
}, function(data, status) {
alert("Ha fallado la petición. Estado HTTP:" + status);
});
}
]);
{{url>http://embed.plnkr.co/pmsJkq}}
===== Malas prácticas =====
En muchos ejemplos que se ven de AngularJS se tiende a definir un provider en un único bloque en vez de definir diversas funciones separadas como hemos hecho aqui.
Podemos ver el siguiente trazo de código en el que se ha definido el provider usando varias funciones anónimas anidadas.
app.provider("remoteResource", function() {
var _baseUrl;
this.setBaseUrl = function(baseUrl) {
_baseUrl = baseUrl;
}
this.$get = ['$http',
function($http) {
return new function($http, baseUrl) {
this.get = function(fnOK, fnError) {
$http({
method: 'GET',
url: baseUrl + '/datos.json'
}).success(function(data, status, headers, config) {
fnOK(data);
}).error(function(data, status, headers, config) {
fnError(data, status);
});
}
}($http, _baseUrl);
}
];
});
Este código - aunque funciona - no es recomendable por lo complejo que resulta de entender.
===== Referencias =====
* [[https://github.com/johnpapa/angularjs-styleguide/blob/master/README.md|AngularJS Style Guide by @john_papa]]
* [[https://github.com/toddmotto/angularjs-styleguide/blob/master/README.md|AngularJS styleguide by @toddmotto]]