Tutorial sobre AngularJS: (6) Scope
Si has leído el capítulo de arquitectura, quizá te preguntes cómo enlazar la vista con tus datos, para ello en Angularjs se utiliza el $scope. Los puntos que vamos a abordar en este capítulo son:
- Separación del modelo de datos.
- Herencia de scopes.
- Principales métodos y propiedades de scope.
- Eventos
- Isolated Scope
- Depuración
Separación del modelo de datos.
Como en cualquier aplicación SPA con Angularjs nuestros datos en un porcentaje muy alto provienen de servicios RESTful tanto propios como de terceros y puesto que Angular permite hacer binding a datos, a expresiones y a métodos. Como buena práctica siempre tienes que separar tus datos del resto de la información, por una sencilla razón evitar transportar información innecesaria tanto desde el cliente al servidor como de este hacia el cliente, así como el no transporte de nulos, ya que estudios que hemos realizado, sino transportamos nulos ahorramos más de un 25% de tráfico de red.
Os voy a poner un sencillo ejemplo y para ello voy a partir de una entidad muy sencilla donde vamos a tener dos propiedades(name y gender). así como un botón que simula el envío de estos datos al servidor.
Si analizamos el código de nuestro controlador, se puede observar que tenemos un array de genders.
Una método save que guarda en la propiedad text el nombre y el id del género.
Por último un modelo que tiene un name y un gender, y es en este momento cuando te tienes que preguntar el porqué de esa separación y sobre todo el porqué de ese valor null en name.
1. El modelo está separado para evitar enviar la propiedad text y el array genders en cada petición al servidor.
2. El modelo tiene la propiedad name establecida a null y para qué, pues para lo único que te sirve es para consumir ancho de banda, con lo cual si el modelo nos llega del servidor bastaría con haber enviado esto desde el servidor.
{gender:1}
Puesto que Angular si una propiedad no está establecida funciona perfectamente y cuando cambie el valor automáticamente se completará nuestro modelo.
Una vez que ya tenemos definido nuestro scope en nuestro controlador vamos a ver cómo ligamos este a la vista.
Aunque tendremos un capítulo completo de binding vamos a analizar cada una de las directivas que hemos utilizado.
ng-model. Lo estamos utilizando en nuestro input para establecer un Two Binding con la propiedad name de nuestro model.
También hacemos lo mismo en el select para la propiedad gender.
ng-options. Nos permite enlazar nuestro select con el array de genders.
ng-click. Permite hacer la llamada a la función save de nuestro scope y establecer la propiedad text.
Por último y mediante interpolación hacemos One Binding entre la propiedad text de nuestro scope con el span.
Herencia de Scope
Cuando creamos una app Angular en el bootstrap se establece un scope principal que es $rootScope y todos los demás scopes heredan de este. He visto en muchas app’s la introducción de información en este objeto y para mí, aunque se puede hacer lo veo como una mala práctica, ya que puede ser que esa propiedad o método no los necesites en un determinado sitio y no hace otra cosa que meter ruido, para ello utiliza una constante o un value e inyectalo cuando lo necesites.
La anidación de scopes es una de las cosas más potentes que tiene Angular y que permite tener un modelo compartido entre diferentes controladores y después cosas específicas dentro de cada uno de ellos.
Imagina que tienes que hacer una página donde definir el perfil de un usuario de tu aplicación. La puedes diseñar con carpetas y hacer una sola lectura para traer toda la información de tu usuario y en cada carpeta tener un botón save específico para guardar exclusivamente el contenido de esa carpeta.
Si analizamos el código tenemos tres controladores UserController responsable de recuperar la información del usuario, UserPersonalDataController responsable de guardar los datos personales de nuestro usuario y por último UserPassWordDataController responsable de guardar los datos de password de nuestro user. Gracias a la potencia de la anidación de scopes podemos desde cualquiera de los scopes hijos tener acceso a los datos del scope padre.
Observa en la vista como userController anida a los controladores userPersonalDataController y userPassWordDataController.
Principales métodos y propiedades de scope
$id. Nos devuelve un identificador único para cada scope.
$parent. Devuelve el scope padre o null si es el scope principal o $rootScope.
$new. Crea un nuevo scope.
$apply. Aplica cualquier cambio realizado en el modelo, llama a $digest. No se recomienda su uso en un controlador, se utiliza normalmente en una directiva, para avisar a Angular que refresque la vista, por ejemplo ante una respuesta de un evento del DOM.
$digest. Es llamada desde $apply y establece $$phase en un valor, si se llama a esta función y $$phase está establecida se lanza una exception. no se recomienda su uso directo, puesto que siempre que se llama a $apply se ejecuta $digest.
$$phase. Puede tener un valor null o el valor establecido por $digest y controla que está en un proceso de refresco de vista.
$eval. Evalúa una expresión.
$evalAsync. Evalúa una expresión al final del ciclo de refresco de la vista.
$destroy. Establece todos los valores del scope a null y lanza un evento al que te puedes suscribir con scope.on(‘destroy’,function(){…}).
$watch. Observa una propiedad o expresión, evita su uso por temas de performance.
$watchGroup. Es muy parecida a $watch pero permite observar un array de expresiones.
$watchCollection. Observa los cambios en una collección.
Estas últimas propiedades imagino que serán eliminadas en Angular 2.0 y serán sustituidas por Object.Observe y Array.Observe y de esta forma se eliminará el mito de dirty checking vs observable.
Te recomiendo que leas la referencia de scope y amplies tus conocimientos de cada una de ellas, el resto de propiedades y que puedes ver en depuración con el prefijo $$ son privadas y no se recomienda su uso.
Eventos.
Como en cualquier sistema, cuando hablamos de eventos tienes que tener en cuenta dos puntos:
- La suscripción a un evento
- La emisión del evento.
Para suscribirnos a un evento en Angular es tan sencillo como utilizar $scope.on.
var listener = $scope.on(‘evento’,function(){…});
Una vez que te suscribes si quieres hacer detach del evento no tienes más que ejecutar
listener();
La emisión de eventos la puedes hacer de dos formas.
1. $emit. Emite un evento al que se pueden suscribir todos $copes anidados hasta el $rootScope, si un usuario en uno de los $scope cancela el evento deja de propagarse en el sistema de herencia.
2. $broadcast. Emite un evento de forma global y te puedes suscribir en un servicio,factoria,directiva,run,etc.. Un ejemplo donde nosotros lo utilizamos es para controlar en grandes app’s el número de templates cargadas e ir descargando cuando pasan de un número determinado, suscribiendonos al evento $routeChangeStart.
Te recomendamos que amplies la información en el siguiente link.
Eventos en Angularjs
Isolated Scope.
Este tipo de scope, solamente se utiliza en las directivas y lo veremos a fondo en el capítulo destinado a estas, pero piensa que no es otra cosa que un SandBox para nuestra directiva, pudiendo hacer One Binding, Two Binding y binging a métodos desde nuestra directiva.
Using Isolated Scope with Attributes
Depuración
Aunque sería como un sueño el no tener que depurar una aplicación, entre otras cosas por el tiempo que se pierde, la realidad te dice que ese sueño no se cumple y por tanto tenemos que saber cómo depurar nuestra aplicación Angularjs.
Lo ideal es hacerlo desde Chrome y utilizar Batarang con esta herramienta se ahorra bastante tiempo a la hora de depurar una app. Os paso una muestra de los scopes creados en nuestro ejemplo.
Si tienes instalada Batarang y quieres trabajar desde la consola, también lo puedes hacer de la siguiente forma.
1. Selecciona un elemento del Dom.
2. Desde la consola escribe $scope.
Si estás utilizando otro explorador distinto a Chrome puedes hacer lo siguiente.
1. Selecciona un elemento del DOM.
2. Teclea angular.element($0).scope().
Muestra en Firefox
Muestra en IE
Te recomendamos que amplies conocimientos sobre los Command Line Api en el siguiente link.
Command Line API Reference
@_PedroHurtado y @XaviPaper
Vínculo hacia el capitulo anterior: (5) Controladores
… y hacia el siguiente: (7) Bindings