AngularJS Book: Chapter 2: Guía rápida de Angular

[Fuente: AngularJS Book –  www.it-ebooks.info E:DOCSDEVANGULARJSig103_14AngularJS]

[Ejemplos en pruebas/workspace/angularjs-test]

Invocando Angular

Todas las aplicaciones que quieran utilizas Angular deben hacer dos cosas:

  • Referenciar la libreria angular.js
  • Decirle a Angular qué parte del DOM debe gestionar con la directiva ng-app

Referenciando la librería angular

  • Es mejor utilizar un CDN para que los usuarios puedan cachear esta librería cuando se usa en varias apps.
  • El CDN de Google:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>

Declarando el ámbito de funcionamiento de angular con ng-app

  • Si queremos que Angular maneje todo el DOM:
<html ng-app>
---
</html>
  • Si tienes que meter Angular en una aplicación existente donde hay ya otra tecnología que maneja el DOM, podemos declarar que Angular sólo maneje parte de la página colocando la directiva ng-app en un un elemento div de la página:
<html>
...
<div ng-app>
...
</div>
...
</html>

Modelo Vista Controlador

  • AngularJS soporta el estilo de Modelo-Vista-Controlador en el diseño de las aplicaciones
  • Por tanto lo mejor es adaptar el diseño de las apps a este modelo donde siempre encontraremos lo siguiente:
    • Un modelo conteniendo los datos que representan el estado actual de tu aplicación
    • Vistas que muestran esos datos
    • Controladores que manejan las relaciones entre tu modelo y tus vistas
  • El modelo se crea utilizando atributos de objeto , o incluso tipos primitivos conteniendo los datos. Por ejemplo si queremos pintar un texto al usuario , podríamos tener una cadena como la siguiente:
var someTexr = 'hola caracola';
  • Para crear tus vistas es tan simple como crear páginas HTML que se fusionarán con tu modelo. Dentro del HTML podemos invocar a datos del modelo con las  dobles llaves:
<p>{{someText}}</p>
  • Los controladores son clases o tipos que se hacen para decirle a Angular qué objetos o primitivas son los que forman parte de tu modelo.Esto se hace asignando los mismos al objeto $scope pasado como parámetro del controlador:
function TextController ($scope) {
  $scope.someText = someText;
}
  • Poniendolo todo junto tenemos
<html ng-app>
<body ng-controller="TextController">
	<p>{{someText}}</p>

	<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>

	<script>
		function TextController ($scope) {
		  $scope.someText = "Hola caracola";
		}
	</script>

</body>
</html>

Cargando esta página en un navegador , veremos:

Hola caracola
  • Aunque esta forma de definir el modelo  , al estilo tipo primitivo , es válida, para la mayoría de las aplicaciones es mejor crear un objeto modelo que sirva de contenedor. Asi , por ejemplo , las variables de modelo del ejemplo anterior podrían ser asi:
var messages = {};
messages.someText = 'Hola caracola';
function TextController ($scope) {
	$scope.messages = messages;
}

y en la plantilla invocamos al modelo de la siguiente forma:

<p>{{messages.someText}}</p>
  • Como veremos más adelante , cuando hablemos del objecto $scope , crear el modelo de esta forma previene de comportamiento inesperado causado por la herencia de prototipos en los objetos $scope.
  • Sobre el controlador , también añadir , que aunque en el ejemplo hemos incluido el TextController en el ámbito global, la forma correcta de definir un controlador es como parte de lo que llamaremos un “módulo” , lo cual proporciona un namespace para partes relacionadas de nuestra app. Con un módulo el ejemplo quedaría así:
<html ng-app = 'myApp'>
<body ng-controller="TextController">
<p>{{messages.someText}}</p>

	<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>

	<script>
		var myAppModule = angular.module('myApp',[]);
		myAppModule.controller('TextController',
			function ($scope) {
				var messages = {};
				messages.someText = 'Hola caracola';
				$scope.messages = messages;
			}
		);
	</script>

</body>
</html>
  • Más adelante explicaremos los porques y los comos de trabajar con módulos. Por ahora , sólo recuerda que es una buena costumbre mantener las cosas fuera del namespace lgobal y que el mecanismo para hacerlo son los módulos.

Las plantillas (templates) y el data binding

Mostrar texto en las plantillas

  • La forma de mostrar o actualizar texto en las plantillas es utilizando la directiva ng-bind . Esta directiva tiene dos formas equivalentes:
    • Con dobles llaves: <p>{{greeting}}</p>
    • Utilizando la directiva ng-bind: <p ng-bind=”greeting”></p>
  • Las dos formas producen salidas equivalentes, pero hay que decir que en las páginas de entradas a las aplicaciones (index.html) con la sintaxis de las dobles llaves el usuario  , la primera vez de carga del index.html , verá el {{texto}} y con el sintaxis de directiva el usuario no verá nada

Formularios de entradas de datos

  • Trabajar con elementos de formulario en Angular es simple. Como hemos visto en ejemplos anteriores podemos enlazar elementos con el atributo ng-model con atributos del modelo.Esto funciona para todos los tipos de elementos de formulario: text input , radio buttons , checkboxes , etc. Por ejemplo podemos enlazar un checkbox con una property de la siguiente forma:
<form ng-controller="SomeController">
<input type="checkbox" ng-model="youCheckedIt" />
</form>
  • Con este código lo que se hace por detrás es lo siguiente:
    • Cuando el usuario marca la casilla, una property llamada “youCheckedIt” en el $scope del SomeController se pone a true. Si la desmarcamos la pondremos a false.
    • Si ponemos $scope.youCheckedIt a true en el SomeController , entonces la casilla aparecerá marcada en el UI. Y si la ponemos a false aparecerá desmarcada
  • Digamos que ahora queremos realizar algo cuando el usuario cambia algo, En los elementos input podemos utilizar la directiva ng-change para especificar un controlador que debe ser invocado cuando el usuario cambia le valor del input. Por ejemplo , hagamos una simple calculadora para orientar a los emprendedores de cuanto dinero deben invertir:
<form ng-controller="StartUpController">
	Starting: <input ng-change="computeNeeded()" 
	              	 ng-model="funding.startingEstimate">
	Recommmendation: {{funding.needed}}
</form>
  • Como aproximación realista , digamos que calculamos  10 veces la estimación del usuario. Y ponemos un valor de cero por defecto:
function StartUpController($scope) {
	$scope.funding = { startingEstimate: 0};
	$scope.computeNeeded = function() {
		$scope.funding.needed = $scope.funding.startingEstimate * 10;
	};
}
  •  Hay un problema potencial con esta estrategía: el valor estimado se recalcula sólo cuando el usuario escribe alguna cantidad en la caja de texto. Pero, ¿qué pasa si el dato recalculado debe cambiar dependiendo de datos que vienen del servidor?
  • Para actualizar un campo sin importar cómo, podemos utilizar una función del $scope llamada $watch:
function StartUpController($scope) {
	$scope.funding = { startingEstimate: 0};
	$scope.computeNeeded = function() {
		$scope.needed = $scope.funding.startingEstimate * 10;
	};

	$scope.$watch('funding.startingEstimate',  computeNeeded);
}
  • Entonces podemos reescribir el template de la siguiente forma:
<form ng-controller="StartUpController">
	Starting: <input  ng-model="funding.startingEstimate">
	Recommmendation: {{funding.needed}}
</form>
  • Habrá casos donde no quieres que se hagan acciones en cada cambio , sino que quieres esperar hasta que el usuario diga que se actualice. Por ejemplo al completar un compra o enviar un mensaje de chat.
  • Hay una forma de agrupar los inputs , con la directiva ng-input que especifique la función a llamar cuando se haga submit en el formulario. Asi , en el ejemplo anterior , podemos hacer que se recalcule la estimación cuando el usuario haga click en un botón:
<form ng-submit="requestFunding()" ng-controller="StartUpController">
	Starting: <input ng-change="computeNeeded()" ng-model="funding.startingEstimate">
	Recommmendation: {{funding.needed}}
	<button>Fund my startup!</button>
</form>

function StartUpController($scope) {
	$scope.funding = { startingEstimate: 0};
	$scope.computeNeeded = function() {
		$scope.needed = $scope.funding.startingEstimate * 10;
	};

	$scope.requestFunding = function() {
		window.later("Sorry , please get more customers first.");
	}

	$watch('funding.startingEstimate',  computeNeeded);
}
  • La directiva ng-submit previene automáticamente de que se produzca el POST por defecto cuando se intenta hacer submit del formulario.
  • Se pueden manejar otros eventos con directivas de angular. Por ejemplo para el onclick tenemos la directiva ng-click , para ondblClick , tenemos la bg-dblClick, y asi. Como ejemplo podemos añadir un botón de reset al formulario de nuestro ejemplo:
<form ng-submit="requestFunding()" ng-controller="StartUpController">
	Starting: <input ng-change="computeNeeded()" ng-model="funding.startingEstimate">
	Recommmendation: {{funding.needed}}
	<button>Fund my startup!</button>
	<button ng-click="reset()">Reset</button>
</form>

function StartUpController($scope) {
	$scope.funding = { startingEstimate: 0};
	$scope.computeNeeded = function() {
		$scope.needed = $scope.funding.startingEstimate * 10;
	};

	$scope.requestFunding = function() {
		window.later("Sorry , please get more customers first.");
	}

	$watch('funding.startingEstimate',  computeNeeded);

	$scope.reset = function() {
		$scope.funding.startingEstimate = 0;	
	}

}

Listas,  Tablas y otros elementos de repetición

  • Ejemplo 1: Uso de directiva ng-repeat Queremos mostrar una lista de estudiantes con datos que vienen desde el servidor. Haremos un ejemplo simulando la lista de estudiantes en una variable de modelo:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html ng-app>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
		<title>AngularJS Test : Student list</title>
		<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script>
	</head>
	<body ng-controller='StudentController'>
		<h1>Student List</h1>
		<ul >
			<li ng-repeat='student in students'>
				<a href='/student/view/{{student.id}}'>{{student.name}}</a>
			</li>			
		</ul>
		<button ng-click="insertStudent('Potitorrrr')">Insert Student</button>

		<script>
		function StudentController($scope) {
			$scope.students = [
			                {name: 'Manzanas' , id:'1'},
			                {name: 'Manzanas 2' , id:'2'},
			                {name: 'Manzanas 3' , id:'3'},
			                ];
			$scope.insertStudent = function(name) {
				$scope.students.splice(1,0,{name: name , id:'4'});
			}
		}
		</script>
	</body>
</html>
  • Ejemplo 2: Bucle reeferenciando el índice del elemento en curso con $index. También existen booleanos de angular que te dicen si estás en el primero ($first) , en algún punto en el medio ($middle) o en el último ($last). Siguiendo con nuestro ejemplo vamos a etiquetar los registros de estudiantes con un índice:
		<ul >
			<li ng-repeat='student in students'>
				<span>{{$index + 1}}</span> <a href='/student/view/{{student.id}}'>{{student.name}}</a>
			</li>			
		</ul>

Algunas notas sobre Javascript no obstrusivo

  • En algún momento como programador Javascript  ,alguién igual te dijo que deberías escribir “Javascript no obstrusivo” , y que utilizar event handlers en los click , mousedown dentro del HTML era una mala idea. Pues tenían razón.
  • La idea de Javascript no obstrusivo ha sido interpretado de muchas formas, pero la versión más lógica sigue las siguientes normas:
    • No todos los navegadores soportan Javascript.Permite que todo el mundo pueda ver tu contenido o usar tu app sin la necesidad de ejecutar código en el navegador.
    • Algunos utilizan navegadores que trabajan de forma diferente. Algunos invidentes utilizan screen-readers y algunos usuarios de mobiles no pueden visualizar webs que tengan Javascript.
    • Javascript funciona de forma diferente según las plataformas. IE es el campeón. Hay que añadir código diferente de event-handling según el navegador.
    •  Los event handlers hacen referencia a funciones en el global namespace. Puede causarte dolores de cabeza cuando intentes integrar otras librerías con funciones en el mismo namespace.
    • Los event handlers combinan estructura y comportamiento. Esto hace tu código dificil de mantener , extender y entender.
  • De la mayoría de las formas , la vida fue mejor cuando escribes Javascript en este estilo. Cierto es que se perdió en legibilidad y aumento la complejidad.
  • En vez de declarar tus acciones de event handlers en el elemento sobre el que actua, generalmente tienes que asignar IDs a estos elementos, recuperar la referencia de ese elemento, y configurar event handlers con callbacks
  • En Angular , hemos decidido reexaminar el problema.
  • El mundo ha cambiado desde que estos conceptos nacieron:
    • El punto #1 ya no es cierto para cualquier conjunto de población que haya en el planeta. Si estás ejecutando un navegador sin Javascript , estás limitado a los sites creados en los 90s
    • Sobre el punto #2, los screen-readers modernos han mejorado en ese aspecto. Se pueden hacer UIs buenos siendo a la vez accesibles
    • Los móviles ahora ejecutan Javascript como si fueran ordenadores de sobremesa.
    • Entonces ahora la cuestión es : podemos resolver los puntos #3 y #4 , manteniendo a la vez la legibilidad y simplicidad de la técnica inline
    • Como hemos dicho previamente, para la mayoría inline event handlers, Angular tiene un equivalente en la forma de ng-eventhandler=”expression” donde eventhandler será reemplazado por click , mousedown , change y así.
    • Si quieres ser notificado cuando un usuario hace click sobre un elemento utiliza la directiva ng-click de la siguiente forma:
    • <div ng-click="doSomething()">...</div>
  • Tranquilos , esto no es como el inline html. Las diferencias son:
    • Se comportan de la misma forma en todos los navegadores. Angular se encarga de tratar las diferencias por ti
    • No operan en el global namespace. Las expresiones que especificas con estas directivas pueden sólo acceder a funciones y datos que están en el ámbito del controller del elemento.Este punto es un poco triki , asi que veamos un ejemplo pa ilustrarlo. En una app típica , creamos una nav bar con una content areas que cambia según selecciones una opción del menú en la nav bar. Podríamo escribir el esqueleto para esto como:
  • <div ng-controller="NavController">
     …
     <li ng-click="doSomething()">Something</li>
     …
     </div>
     <div ng-controller="ContentAreaController">
     …
     <div ng-click="doSomething()">...</div>
     …
     </div>

    Aqui tanto los <li> en la navbar como el <div> en la content area invocan una función llamada “doSomething()” cuando el usuario hace click sobre ellos.Como programador, configuras la función a la que van estas llamadas en el código del controlador. Pueden ser funciones con el mismo nombre o diferente:

function NavController($scope) {
 $scope.doSomething = doA;
 }
 function ContentAreaController($scope) {
 $scope.doSomething = doB;
 }
  • Nos queda el punto #5, combinar estructura y comportamiento.Hay una prueba simple para detectar si nuestro sistema sufre de este acoplamiento: ¿podemos crear un test unitario para la lógica de nuestra app que no requiera que el DOM esté presente?. En Angular podemos escribir controllers conteniendo nuestra lógica de negocio sin hacer referencias a el DOM. El problema no estaba en los event handlers, pero sí en la forma en que escribiamos Javascript. Date cuenta que en todos los controladores que hemos visto hasta ahora, en ninguno hay referencias al DOM o a los eventos del DOM en ningún sitio. Todo el trabajo de localizar elementos y manejar eventpos lo hace Angular

Hiding and Showing

  • Para menus , herramientas context-sensitive y en otros muchos casos tenemos que mostrar/ocultar elementos. Con Angular , los cambios UI son manejados desde cambios en el modelo.Tenemos las directivas ng-show y ng-hide
  • Estas directivas funcionan configurando los estilos de los elementos a display:block pare mostrarlos y display:none para esconderlo. Veamos el ejemplo donde queremos montar un panel de control  para Death Ray:
<div ng-controller='DeathrayMenuController'>
<button ng-click='toggleMenu()'>Toggle Menu</button>
<ul ng-show='menuState.show'>
<li ng-click='stun()'>Stun</li>
<li ng-click='disintegrate()'>Disintegrate</li>
<li ng-click='erase()'>Erase from history</li>
</ul>
<div/>
function DeathrayMenuController($scope) {
$scope.menuState.show = false;
$scope.toggleMenu = function() {
$scope.menuState.show = !$scope.menuState.show;
};
// death ray functions left as exercise to reader
}

CSS Classes and Styles

Es obvio que con lo visto hasta ahora se puede dinámicamente configurar clases y estilos de CSS en tu app con tan sólo haciendo un binding con los datos con la notación {{}}.

Se puede incluso componer nombres de clases parciales que se correspondan con los de tu maqueta. Si , por ejemplo , quieres condicionalmente deshabilitar algunos menus , podrías hacer algo como lo siguiente parea visualmente indicárselo al usuario:

Dado el CSS:

.menu-disabled-true {
color: gray;
}

podríamos mostrar la función stun de tu DeathRay como deshabilitada con este código:

<div ng-controller='DeathrayMenuController'>
<ul>
<li class='menu-disabled-{{isDisabled}}' ng-click='stun()'>Stun</li>
...
</ul>
<div/>

donde tu configurarías la propiedad isDisabled a través de tu controlador de la forma apropiada:

function DeathrayMenuController($scope) {
$scope.isDisabled = false;
$scope.stun = function() {
// stun the target, then disable menu to allow regeneration
$scope.isDisabled = 'true';
};
}

Esta técnica funciona igual de bien cuando se combinan inline styles con interpolación , como por ejemplo con style=”{{some expression}}”.

Si esta técnica parece buena , tiene sus inconvenientes. En el ejemplo anterior se ve fácil , pero en aplicaciones web completas tener que leer tanto la maqueta como el Javascript para crear el CSS de forma correcta puede ser inmanejable.

Es por esto que Angular proporciona las directivas ng-class y ng-style.Las dos reciben una expresión. El resultado de evaluar esta expresión puede ser uno de los siguientes:

  • Una cadena representando una lista de clases de CSS separadas por espacios
  • Un array de class names
  • Un mapa de class names a boolean values

Imaginemos que quieres mostrar errores y warnings a tus usuarios en una localización estandar en la cabecera. Utilizando la directiva ng-class, puedes hacer lo siguiente:

.error {
background-color: red;
}
.warning {
background-color: yellow;
}
<div ng-controller='HeaderController'>
...
<div ng-class='{error: isError, warning: isWarning}'>{{messageText}}</div>
…
<button ng-click='showError()'>Simulate Error</button>
<button ng-click='showWarning()'>Simulate Warning</button>
</div>
function HeaderController($scope) {
$scope.isError = false;
$scope.isWarning = false;
$scope.showError = function() {
$scope.messageText = 'This is an error!';
$scope.isError = true;
$scope.isWarning = false;
};
$scope.showWarning = function() {
$scope.messageText = 'Just a warning. Please carry on.';
$scope.isWarning = true;
$scope.isError = false;
};
}

You can even do nifty things like highlighting a selected row in a table. Let’s say we’re
building a restaurant directory and we want to highlight a row that the user clicks on.
In our CSS, we set up the style for a highlighted row:

.selected {
background-color: lightgreen;
}

In the template, we set ng-class to {selected: $index==selectedRow}. This has the
effect of setting the selected class when our model property called selectedRow matches
the ng-repeat’s $index. We’ll also set up an ng-click to notify our controller as to
which row the user clicks:

<table ng-controller='RestaurantTableController'>
<tr ng-repeat='restaurant in directory' ng-click='selectRestaurant($index)'
ng-class='{selected: $index==selectedRow}'>
<td>{{restaurant.name}}</td>
<td>{{restaurant.cuisine}}</td>
</tr>
</table>

In our JavaScript, we just set up some dummy restaurants and create the selectRow
function:

function RestaurantTableController($scope) {
$scope.directory = [{name:'The Handsome Heifer', cuisine:'BBQ'},
{name:'Green's Green Greens', cuisine:'Salads'},
{name:'House of Fine Fish', cuisine:'Seafood'}];
$scope.selectRestaurant = function(row) {
$scope.selectedRow = row;
};
}

Considerations for src and href Attributes

When data binding to an <img> or <a> tag, the obvious path of using {{ }} in the src or
href attributes won’t work well. Because browsers are aggressive about loading images
parallel to other content, Angular doesn’t get a chance to intercept data binding requests.
While the obvious syntax for an <img> might be:

<img src="/images/cats/{{favoriteCat}}">

Instead, you should use the ng-src attribute and write your template as:

<img ng-src="/images/cats/{{favoriteCat}}">

Similarly, for the <a> tag, you should use ng-href:

<a ng-href="/shop/category={{numberOfBalloons}}">some text</a>

Expressions

The goal behind the expressions that you use in templates is to let you be as clever as
you need to be to create hooks between your template, your application logic, and your
data, but at the same time prevent application logic from sneaking into the template.
Until this point, we’ve been mostly using references to data primitives as the expressions
passed to Angular directives. But these expressions can do much more. You can do simple math (+, -, /, *, %), make comparisons (==, !=, >, <, >=, ⇐), perform boolean logic (&&, ||, !) and bitwise operations (^, &, |). You can call functions you expose on $scope in your controller and you can reference arrays and object notation ([ ], { }, .).

All of these are valid examples of expressions:

<div ng-controller='SomeController'>
<div>{{recompute() / 10}}</div>
<ul ng-repeat='thing in things'>
<li ng-class='{highlight: $index % 4 >= threshold($index)}'>
{{otherFunction($index)}}
</li>
</ul>
</div>

The first expression here, recompute() / 10, while valid, is a good example of putting
logic in the template, and should be avoided. Keeping a separation of responsibilities
between your view and controllers ensures that they’re easy to reason and easy to test.
While you can do quite a lot with expressions, they are computed with a custom parser
that’s part of Angular. They are not evaluated using JavaScript’s eval(), and are considerably more restrictive.

Instead, they are evaluated using a custom parser that comes with Angular. In it, you
won’t find looping constructs (for, while, and so on), flow-of-control operators (if-else,
throw) or operators that modify data (++, –). When you need these types of operations,
do them in your controller or via a directive.

Though expressions are more restrictive than JavaScript in many ways, they are more
forgiving to undefined and null. Instead of throwing a NullPointerException error,
templates will simply render nothing. This allows you to safely use model values that haven’t been set yet, and have them appear in the UI as soon as they get populated.

Separating UI Responsibilities with Controllers

Controllers have three responsibilities in your app:
• Set up the initial state in your application’s model
• Expose model and functions to the view (UI template) through $scope
• Watch other parts of the model for changes and take action

We’ve seen many examples of the first two in this chapter already. We’ll get to that last
one in a bit. The conceptual purpose of controllers, however, is to provide the code or
logic to execute the user’s wishes as they interact with the view.

To keep your controllers small and manageable, our recommendation is that you create one controller per functional area in your view. That is, if you have a menu, create a MenuController. If you have a navigational breadcrumb, write a BreadcrumbControl ler, and so on.You’re probably starting to get the picture, but to be explicit, controllers are tied to a specific piece of the DOM that they’re in charge of managing.

The two main ways of associating a controller with a DOM node are specifying it in the template by declaring it in an ng-controller attribute, and associating it with a dynamically loadable DOM template fragment, called a view, through a route.We’ll talk about views and routes later in this chapter.

If you have complex sections of your UI, you can keep your code simple and maintainable,by creating nested controllers that can share model and functions through an inheritance tree. Nesting controllers is simple; you do it by simply assigning a controller to a DOM element that is inside another one, like so:

<div ng-controller="ParentController">
<div ng-controller="ChildController">...</div>
</div>

Though we express this as nested controllers, the actual nesting happens in scopes. The $scope passed to a nested controller prototypically inherits from its parent controller’s $scope. In this case, this means that the $scope passed to ChildController will have access to all the properties of the $scope passed to ParentController.

Publishing Model Data with Scopes

The $scope object passed to our controllers is the mechanism we use to expose model
data to views. You may have other data in your application, but Angular only considers
it part of the model when it can reach these properties through a scope. You can think
of scopes as a context that you use to make changes to your model observable.

We’ve seen many examples of setting up scopes explicitly, as in $scope.count = 5.
There are also some indirect ways to set up the model from the template itself. You can
do so in the following ways:

  • Through an expression. Since expressions execute in the context of the controller’s scope associated with their element, setting properties in expressions is the same as setting a property of the controller’s scope. That is, doing this:
<button ng-click='count=3'>Set count to three</button>

has the same effect as doing this:

<div ng-controller='CountController'>
<button ng-click='setCount()'>Set count to three</button>
</div>

with your CountController defined as:

function CountController($scope) {
$scope.setCount = function() {
$scope.count=3;
}
}
  • Using ng-model on a form input. As with expressions, the model specified as the argument for ng-model also works within the scope of the enclosing controller. The one addition is that this creates a bi-directional data binding between the form field state and your specified model.

Observing Model Changes with $watch

Possibly the most used of all scope functions is $watch, which notifies you when parts
of your model change. You can watch individual object properties and computed results
(functions), really anything that could be accessed as a property or computed as a Java‐
Script function. The function’s signature is:

$watch(watchFn, watchAction, deepWatch)

The details of each parameter are as follows:

  • watchFn: This parameter is a string with an Angular expression or a function that returns the current value of the model that you want to watch. This expression will be evaluated multiple times, so you need to make sure that it has no side effects. That is, it can be called multiple times without changing state. For the same reason, watch expressions should be computationally cheap. If you’ve passed in an Angular expression in a string, it will be evaluated against objects available to the scope it’s called on.
  • watchAction: This is the function or expression to be called when the watchFn changes. In the function form, it receives the new and old values of watchFn as well as a reference to the scope. Its signature is function(newValue, oldValue, scope).
  • deepWatch: If set to true, this optional boolean parameter tells Angular to examine each property within the watched object for changes. You’d use this if you wanted to watch individual elements in an array or properties in an object instead of just a simple value. As Angular needs to walk the array or object, this can be computationally expensive if the collection is large.

The $watch function returns a function that will de-register the listener when you no
longer want to receive change notifications.If we wanted to watch a property and then later de-register it, we would use the following:

...
var dereg = $scope.$watch('someModel.someProperty', callbackOnChange());
…
dereg();

Let’s revisit our shopping cart scenario from Chapter 1 for a full example. Let’s say that
we want to apply a $10 discount when the customer adds more than $100 worth of
merchandise to her cart. For a template, we’ll use:

<div ng-controller="CartController">
<div ng-repeat="item in items">
<span>{{item.title}}</span>
<input ng-model="item.quantity">
<span>{{item.price | currency}}</span>
<span>{{item.price * item.quantity | currency}}</span>
</div>
<div>Total: {{totalCart() | currency}}</div>
<div>Discount: {{bill.discount | currency}}</div>
<div>Subtotal: {{subtotal() | currency}}</div>
</div>

With a CartController, it would look like the following:

function CartController($scope) {
$scope.bill = {};
$scope.items = [
{title: 'Paint pots', quantity: 8, price: 3.95},
{title: 'Polka dots', quantity: 17, price: 12.95},
{title: 'Pebbles', quantity: 5, price: 6.95}
];
$scope.totalCart = function() {
var total = 0;
for (var i = 0, len = $scope.items.length; i < len; i++) {
total = total + $scope.items[i].price * $scope.items[i].quantity;
}
return total;
}
$scope.subtotal = function() {
return $scope.totalCart() - $scope.discount;
};
function calculateDiscount(newValue, oldValue, scope) {
$scope.bill.discount = newValue > 100 ? 10 : 0;
}
$scope.$watch($scope.totalCart, calculateDiscount);
}

Notice that at the bottom of CartController, we’ve set up a watch on the value of
totalCart() which we use to sum up the total price for this purchase. Whenever this
value changes, the watch will call calculateDiscount(), and we get to set the discount
to an appropriate value. If the total is $100, we’ll set the discount to $10. Otherwise, the
discount will be $0.

Performance Considerations in watch()

The preceding example executes correctly, but there is a potential problem with performance.Though it isn’t obvious, if you put a debugger breakpoint in totalCart(),
you’d see that it gets called six times to render this page. Though you’d never notice it
in this application, in more complex apps, running it six times could be an issue.

Why six? Three of them we can trace pretty easily, as it runs one time each in:
• The template as {{totalCart() | currency}}
• The subtotal() function
• The $watch() function
Then Angular runs all of these again, bringing us to six. Angular does this to verify that
transitive changes in your model have fully propagated and your model has settled.

Angular does this checking by making a copy of all watched properties and comparing
them to the current value to see if they’ve changed. In fact, Angular may run this up to
ten times to ensure full propagation. If changes are still occurring after ten iterations,
Angular exits with an error. If that occurs, you probably have a dependency loop that
you’ll need to fix.

Though you currently need to worry about this, by the time you’ve finished this book
it may be a non-issue. While Angular has had to implement data binding in JavaScript,
we’ve been working with the TC39 folks on a low-level native implementation called
Object.observe(). With this in place, Angular will automatically use Object.ob
serve() wherever present to give you native-speed data binding.

As you’ll see in the next chapter, Angular has a nice Chrome debugging extension called
Batarang that will automatically highlight expensive data bindings for you.Now that we know about this issue, there are a few ways we can solve it. One way would be to create a $watch on changes to the items array and just recalculate the total, discount,and subtotal as properties on the $scope.To do this, we’d update the template to use these properties:

<div>Total: {{bill.total | currency}}</div>
<div>Discount: {{bill.discount | currency}}</div>
<div>Subtotal: {{bill.subtotal | currency}}</div>

Then, in JavaScript, we’d watch the items array, and call a function to calculate the totals on any change to that array, like so:

function CartController($scope) {
$scope.bill = {};
$scope.items = [
{title: 'Paint pots', quantity: 8, price: 3.95},
{title: 'Polka dots', quantity: 17, price: 12.95},
{title: 'Pebbles', quantity: 5, price: 6.95}
];
var calculateTotals = function() {
var total = 0;
for (var i = 0, len = $scope.items.length; i < len; i++) {
total = total + $scope.items[i].price * $scope.items[i].quantity;
}
$scope.bill.totalCart = total;
$scope.bill.discount = total > 100 ? 10 : 0;
$scope.bill.subtotal = total - $scope.bill.discount;
};
$scope.$watch('items', calculateTotals, true);
}

Notice here that the $watch specified items as a string. This is possible because the
$watch function can take either a function (as we did previously) or a string. If a string
is passed to the $watch function, then it will be evaluated as an expression in the scope
of the $scope it’s called on.

This strategy might work well for your app. However, since we’re watching the items
array, Angular will have to make a copy of it to compare it for us. For a large list of items,
it may perform better if we just recalculate the bill properties every time Angular evaluates the page. We can do this by creating a $watch with only a watchFn that will recalculate our properties like this:

$scope.$watch(function() {
var total = 0;
for (var i = 0; i < $scope.items.length; i++) {
total = total + $scope.items[i].price * $scope.items[i].quantity;
}
$scope.bill.totalCart = total;
$scope.bill.discount = total > 100 ? 10 : 0;
$scope.bill.subtotal = total - $scope.bill.discount;
});

Watching multiple things

What if you want to watch multiple properties or objects and execute a function whenever any of them change? You’d have two basic options:
• Put them into an array or object and pass in deepWatch as true.
• Watch a concatenated value of the properties.

In the first case, if you’ve got an object with two properties a and b in your scope, and
want to execute the callMe() function on change, you could watch both of them, like
so:

$scope.$watch('things.a + things.b', callMe(...));

Of course, a and b could be on different objects, and you could make the list as long as
you like. If the list is long, you would likely write a function that returns the concatenated
value rather than relying on an expression for the logic.

In the second case, you might want to watch all the properties on the things object. In
this case, you could do this:

$scope.$watch('things', callMe(...), true);

Here, passing in true as the third parameter asks Angular to walk the properties of
things and call callMe() on a change to any of them. This works equally well on an
array as it does here on an object.

Organizing Dependencies with Modules

En aplicaciones ya algo complejas , organizar toda la funcionalidad de tu código en áreas de responsabilidad es a menudo una ardua tarea. Hemos visto como los controladores nos proporcionan un espacio para poner código que expone las funciones y los datos que corresponden al template de la vista. Pero que hay sobre el resto del código en el que necesitamos apoyarnos en nuestras aplicaciones? El lugar más obvio para poner esto sería en funciones dentro del controlador.

Esto funciona bien en aplicaciones pequeñas y en los ejemplos sobre los que estamos trabajando , pero rápidamente se convierte en inmanejable en aplicaciones reales.Los controladores terminarían siendo difíciles de mantener.

Introducir módulos.  Con los módulos tenemos una forma de agrupar dependencias para una área funcional dentro de nuestra aplicación , y un mecanismo para automáticamente resolver dependencias (también conocido como inyección de dependencias). Genéricamente  llamamos a estas dependencias servicios , ya que proporcionan servicios específicos a nuestra aplicación.

Por ejemplo , si en nuestra web de tienda online un controller necesita sacar una lista de items de venta desde el servidor , querríamos tener algún objeto – llamemosle Items – que se encarga de recoger los datos desde el servidor. El objeto Items , por su parte , necesita alguna forma de comunicar con la base de datos en el servidor a través de XHR o WebSockets. Haciendo esto con módulos tendría el siguiente aspecto:

function ItemsViewController($scope) {
// make request to server
…
// parse response into Item objects
…
// set Items array on $scope so the view can display it
...
}

Aunque esto funciona , tiene algunos problemas potenciales:

  • So algún otro controller también necesita sacar los Items desde el servidor, tendriamos que replicar el código –> MALO para mantener el código
  • Con otros factores como autenticación, parsing complexity, etc se hace dificil delimitar las fronteras de responsabilidad de este objeto controller y la lectura del código se hace dificil.
  • To unit test this bit of code, we’d either need to actually have a server running, or monkey patch XMLHttpRequest to return mock data. Having to run the server will make tests very slow, it’s a pain to set up, and it usually introduces flakiness into tests. The monkey patching route solves the speed and flakiness problems, but it means you have to remember to un-patch any patched objects between tests, and it brings additional complexity and brittleness by forcing you to specify the exact on-the-wire format for your data (and in having to update the tests whenever this format changes).

Con módulos , y la inyección de dependencia que conseguimos de ellos , podemos codificar nuestro controller de manera más simple:

function ShoppingController($scope, Items) {
$scope.items = Items.query();
}

Probablemente,  te estes preguntando a ti mismo de donde viene Items. Pues en ese código se asume que hemos definido Items como un service.

Services son objetos singleton (single-instance) que se encargan de las tareas necesarias que apoyan la funcionalidad de tu aplicación. Angular viene con muchos servicios como $location , para interactuar con el objeto location del navegador , $route , para intercambiar entre vistas basadas en los cambios del location (URL) y $http para comunicar con servidores.

Puedes , y deberias , crear tus propios servicios para encapsular todas las treas únicas a tu aplicación. Los servicios pueden ser compartidos para todos los controllers que los necesiten. De hecho , son un buen mecanismo a utilizar cuando necesitas comunicación entre controllers y compartir estado.

Los servicios que lleva angular por defecto comienzan con $, así que es buena idea que no empiecen por $ los servicios que te crees para evitar colisiones.

Se definen módulos con el module object’s API. Hay tres funciones para crear servicios genéricos, con diferentes niveles de complejidad y habilidad:

  • provider(name, Object OR constructor() ) : 
    A configurable service with complex creation logic. If you pass an Object, it should have a function named $get that returns an instance of the service. Otherwise, Angular assumes you’ve passed a constructor that, when called, creates the instance.
  • factory(name, $getFunction() )
    A non-configurable service with complex creation logic. You specify a function that, when called, returns the service instance. You could think of this as provider(name, { $get: $getFunction() } ).
  • service(name, constructor() )
    A non-configurable service with simple creation logic. Like the constructor option with provider,Angular calls it to create the service instance.

We’ll look at the configuration option for provider() later, but let’s discuss an example
with factory() for our preceding Items example. We can write the service like this:

// Create a module to support our shopping views
var shoppingModule = angular.module('ShoppingModule', []);
// Set up the service factory to create our Items interface to the
// server-side database
shoppingModule.factory('Items', function() {
var items = {};
items.query = function() {
// In real apps, we'd pull this data from the server...
return [
{title: 'Paint pots', description: 'Pots full of paint', price: 3.95},
{title: 'Polka dots', description: 'Dots with polka, price: 2.95},
{title: 'Pebbles', description: 'Just little rocks', price: 6.95}
];
};
return items;
});

When Angular creates the ShoppingController, it will pass in $scope and the new
Items service that we’ve just defined. This is done by parameter name matching. That
is, Angular looks at the function signature for our ShoppingController class, and
notices that it is asking for an Items object. Since we’ve defined Items as a service, it
knows where to get it.

The result of looking up these dependencies as strings means that the arguments of
injectable functions like controller constructors are order-independent. So instead of
this:

function ShoppingController($scope, Items) {...}

we can write this:

function ShoppingController(Items, $scope) {...}

and it all still functions as we intended.

To get this to work with our template, we need to tell the ng-app directive the name of
our module, like the following:

<html ng-app='ShoppingModule'>

To complete the example, we could implement the rest of the template as:

<body ng-controller="ShoppingController">
 <h1>Shop!</h1>
 <table>
 <tr ng-repeat='item in items'>
 <td>{{item.title}}</td>
 <td>{{item.description}}</td>
 <td>{{item.price | currency}}</td>
 </tr>
 </table>

How Many Modules Do I Need?

As services themselves can have dependencies, the Module API lets you define dependencies for your dependencies.In most applications, it will work well enough to create a single module for all the code you create and put all of your dependencies in it.

If you use services or directives from third-party libraries, they’ll come with their own modules. As your app depends on them, you’d refer to them as dependencies of your application’s module.For instance, if you include the (fictitious) modules SnazzyUIWidgets and SuperData‐Sync, your application’s module declaration would look like this:

var appMod = angular.module('app', ['SnazzyUIWidgets', 'SuperDataSync'];

Formatting Data with Filters

Filters allow you to declare how to transform data for display to the user within an
interpolation in your template. The syntax for using filters is:

{{ expression | filterName : parameter1 : ...parameterN }}

where expression is any Angular expression, filterName is the name of the filter you
want to use, and the parameters to the filter are separated by colons. The parameters
themselves can be any valid Angular expression.

Angular comes with several filters, like currency, which we’ve seen:

{{12.9 | currency}}

This bit of code will display the following:

$12.90

We put this declaration in the view (rather than in the controller or model) because the
dollar sign in front of the number is only important to humans, and not to the logic we
use to process the number.

Other filters that come with Angular include date, number, uppercase, and more.
Filters can also be chained with additional pipe symbols in the binding. For example,
we can format the previous example for no digits after the decimal by adding the number
filter, which takes the number of decimals to round to as a parameter. So:

{{12.9 | currency | number:0 }}

displays:

$13

You’re not limited to the bundled filters, and it is simple to write your own. If we wanted to create a filter that title-cased strings for our headings, for example, we could do so as follows:

var homeModule = angular.module('HomeModule', []);
homeModule.filter('titleCase', function() {
var titleCaseFilter = function(input) {
var words = input.split(' ');
for (var i = 0; i < words.length; i++) {
words[i] = words[i].charAt(0).toUpperCase() + words[i].slice(1);
}
return words.join(' ');
};
return titleCaseFilter;
});

With a template like this:

<body ng-app='HomeModule' ng-controller="HomeController">
<h1>{{pageHeading | titleCase}}</h1>
</body>

and inserting the pageHeading as a model variable via a controller:

function HomeController($scope) {
$scope.pageHeading = 'behold the majesty of your page title';
}

Changing Views with Routes and $location

Though AJAX apps are technically single-page apps (in the sense that they only load an
HTML page on the first request, and then just update areas within the DOM thereafter),
we usually have multiple sub-page views that we show or hide from the user, as appropriate.

We can use Angular’s $route service to manage this scenario for us. Routes let you
specify that, for a given URL that the browser points to, Angular should load and display a template, and instantiate a controller to provide context for the template.

You create routes in your application by calling functions on the $routeProvider service as a configuration block. It goes something like this pseudo-code:

var someModule = angular.module('someModule', [...module dependencies...])
someModule.config(function($routeProvider) {
$routeProvider.
when('url', {controller:aController, templateUrl:'/path/to/tempate'}).
when(...other mappings for your app...).
…
otherwise(...what to do if nothing else matches...);
)};

The preceding code says that when the browser’s URL changes to the specified URL, Angular will load the template in /path/to/template, and associate the root element of this template with aController (as if we’d typed ng-controller=aController).

The otherwise() call in the last line tells the route where to go if nothing else matches.
Let’s put it to use. We’re building an email app that will easily win out over Gmail,
Hotmail, and all the others. We’ll call it…A-Mail. For now, let’s start simply. We’ll have
a first view that displays a list of email messages with a date, title, and the sender. When
you click a message, it should show you a new view with the body of that message.
Due to browser security restrictions, if you’re trying the code out yourself,
you’ll need to serve it from a web server instead of just file://. If you
have python installed, you could serve it by executing python -m Sim
pleHTTPServer 8888 from your working directory.

For the main template, we’ll do something a bit different. Instead of putting everything
in the first page loaded, we’ll just create a layout template that we’ll put our views into.
We’ll place everything that persists from view to view, like our menus, here. In this case,
we’ll just display a heading with the name of our app. We’ll then use the ng-view directive to tell Angular where we want our views to appear.

index.html

<html ng-app="AMail">
<head>
<script src="src/angular.js"></script>
<script src="src/controllers.js"></script>
</head>
<body>
<h1>A-Mail</h1>
<div ng-view></div>
</body>
</html>

As our view templates will be inserted into the shell we just created, we can write them
as partial HTML documents. For the email list, we’ll use ng-repeat to iterate through
a list of messages and render them into a table.

list.html

<table>
<tr>
<td><strong>Sender</strong></td>
<td><strong>Subject</strong></td>
<td><strong>Date</strong></td>
</tr>
<tr ng-repeat='message in messages'>
<td>{{message.sender}}</td>
<td><a href='#/view/{{message.id}}'>{{message.subject}}</td>
<td>{{message.date}}</td>
</tr>
</table>

Notice here that we’re going to let the user navigate to a particular message by clicking
on the subject. We’ve data bound the URL to message.id, so clicking on a message with
id=1 will take the user to /#/view/1. We’ll use this navigation-by-url, also known as deeplinking,in the message detail view’s controller, to make a particular message available to the detail view.

To create this message detail view, we’ll create a template that displays properties from
a single message object.
detail.html

<div>
	<strong>Subject:</strong> {{message.subject}}
</div>
<div>
	<strong>Sender:</strong> {{message.sender}}
</div>
<div>
	<strong>Date:</strong> {{message.date}}
</div>
<div>
	<strong>To:</strong> <span ng-repeat='recipient in message.recipients'>{{recipient}}
	</span>
	<div>{{message.message}}</div>
	<a href='#/'>Back to message list</a>
</div>

Now, to associate these templates with some controllers, we’ll configure the $routePro
vider with the URLs that invoke our controllers and templates.

controllers.js

//A-MAIL
// Create a module for our core AMail services
var aMailServices = angular.module('AMail', []);

// Set up our mappings between URLs, templates, and controllers
function emailRouteConfig($routeProvider) {
	// Notice that for the detail view, we specify a parameterized URL component
	// by placing a colon in front of the id
	$routeProvider.when('/', {
							controller: ListController,
							templateUrl: 'list.html'
							}).
					when('/view/:id', {
							controller: DetailController,
							templateUrl: 'detail.html'
							}).
					otherwise({
						redirectTo: '/'
					});
}

// Set up our route so the AMail service can find it
aMailServices.config(emailRouteConfig);

// Some fake emails
messages = [
            {
			id: 0, sender: 'jean@somecompany.com', subject: 'Hi there, old friend',
			date: 'Dec 7, 2013 12:32:00', recipients: ['greg@somecompany.com'],
			message: 'Hey, we should get together for lunch sometime and catch up.'
						+'There are many things we should collaborate on this year.'
			}, 
			{
			id: 1, sender: 'maria@somecompany.com',
			subject: 'Where did you leave my laptop?',
			date: 'Dec 7, 2013 8:15:12', recipients: ['greg@somecompany.com'],
			message: 'I thought you were going to put it in my desk drawer.'
			+'But it does not seem to be there.'
			}, {
			id: 2, sender: 'bill@somecompany.com', subject: 'Lost python',
			date: 'Dec 6, 2013 20:35:02', recipients: ['greg@somecompany.com'],
			message: "Nobody panic, but my pet python is missing from her cage.'
			+'She doesn't move too fast, so just call me if you see her."
			} 
	];

// Publish our messages for the list template
function ListController($scope) {
	$scope.messages = messages;
}

// Get the message id from the route (parsed from the URL) and use it to
// find the right message object.
function DetailController($scope, $routeParams) {
	$scope.message = messages[$routeParams.id];
}

We’ve created the basic structure for an app with many views. We switch views by
changing the URL. This means that the forward and back buttons just work for users.
Users are able to bookmark and email links to views within the app, even though there
is only one real HTML page.

Talking to Servers

Okay, enough messing around. Real apps generally talk to real servers. Mobile apps and the emerging Chrome desktop apps may be exceptions, but for everything else, whether you want persistence in the cloud or real-time interactions with other users, you probably want your app to talk to a server.

For this, Angular provides a service called $http. It has an extensive list of abstractions
that make it easier to talk to servers. It supports vanilla HTTP, JSONP, and CORS. It includes security provisions to protect from both JSON vulnerabilities and XSRF. It lets you easily transform the request and response data, and it even implements simple caching.

Let’s say we want to retrieve products for our shopping site from a server instead of from
our silly in-memory mocks. Writing the server bits is beyond the scope of this book, so
let’s just imagine that we’ve created a service that will return a list of products as JSON
when you make a query to /products.Given a response that looks like this:

[
{
"id": 0,
"title": "Paint pots",
"description": "Pots full of paint",
"price": 3.95
},
{
"id": 1,
"title": "Polka dots",
"description": "Dots with that polka groove",
"price": 12.95
},
{
"id": 2,
"title": "Pebbles",
"description": "Just little rocks, really",
"price": 6.95
}
...etc...
]

we could write the query like so:

function ShoppingController($scope, $http) {
$http.get('/products').success(function(data, status, headers, config) {
$scope.items = data;
});
}

and use it in a template like this:

<body ng-controller="ShoppingController">
<h1>Shop!</h1>
<table>
<tr ng-repeat="item in items">
<td>{{item.title}}</td>
<td>{{item.description}}</td>
<td>{{item.price | currency}}</td>
</tr>
</table>
</body>

As we learned previously, we would be better off in the long run by delegating the work
of talking to the server to a service that could be shared across controllers. We’ll take a
look at this structure and the full range of $http functions in Chapter 5.

Changing the DOM with Directives

Directives extend HTML syntax, and are the way to associate behavior and DOM transformations with custom elements and attributes. Through them, you can create reusable UI components, configure your application, and do almost anything else you can imagine wanting to do in your UI template.

You can write apps with the built-in directives that come with Angular, but you’ll likely
run into situations where you want to write your own. You’ll know it’s time to break
into directives when you want to deal with browser events or modify the DOM in a way
that isn’t already supported by the built-in directives. This code of yours belongs in a
directive that you write, and not in a controller, service, or any other place in your app.
As with services, you define directives through the module object’s API by calling its
directive() function, where directiveFunction is a factory function that defines
your directive’s features.

var appModule = angular.module('appModule', [...]);
appModule.directive('directiveName', directiveFunction);

Writing the directive factory function is a deep area, and we’ve dedicated an entire
chapter to it in this book. To whet your appetite, though, let’s look at a simple example.

HTML5 has a great new attribute called autofocus that will place keyboard focus on an
input element. You’d use it to let the user start interacting with the element via his
keyboard without having to click on it first. This is great, as it lets you declaratively
specify what you want the browser to do without having to write any JavaScript. But
what if you wanted to place focus on some non-input element, like a link or any div?
And what if you wanted it to work on non-HTML5 browsers as well? We could do it
with a directive.

var appModule = angular.module('app', []);
appModule.directive('ngbkFocus', function() {
return {
link: function(scope, element, attrs, controller) {
element[0].focus();
}
};
});

Here, we’re returning the directive configuration object with its link function specified.
The link function gets a reference to the enclosing scope, the DOM element it lives on,
an array of any attributes passed to the directive, and the controller on the DOM element, if it exists. Here, we only need to get at the element and call its focus() method.
We can then use it in an example like so:

index.html

<html lang='en' ng-app='app'>
...include angular and other scripts...
<body ng-controller="SomeController">
<button ng-click="clickUnfocused()">
Not focused
</button>
<button ngbk-focus ng-click="clickFocused()">
I'm very focused!
</button>
<div>{{message.text}}</div>
</body>
</html>

controllers.js

function SomeController($scope) {
$scope.message = { text: 'nothing clicked yet' };
$scope.clickUnfocused = function() {
$scope.message.text = 'unfocused button clicked';
};
$scope.clickFocused = function {
$scope.message.text = 'focus button clicked';
}
}
var appModule = angular.module('app', ['directives']);

When the page loads, the user will see the button labeled “I’m very focused!” with the
focus highlight. Hitting the spacebar or the enter key will cause a click and invoke the
ng-click, which will set the div text to ‘focus button clicked’. Opening this example in
a browser, we’d see something that looks like:

Sin título

Validating User Input

Angular automatically augments <form> elements with several nice features suitable for
single-page applications (ng-form directive).One of these nice features is that Angular lets you declare valid states for inputs within the form and allow submission only when the entire set of elements is valid.

For example, if we’re creating a signup form where we require entering a name and
email, but have an optional age field, we can validate several user entries before they are submitted to the server. Loading the example that follows into a browser will display
what is shown in Figure 2-5.

Sin título
Figure 2-5. Form validation

We’d want to make sure the user had entered text in the name fields, that he had entered a properly formed email address, and that if he entered an age, it was valid.
We can do this all in the template, using Angular’s extensions to <form> and the various
input elements, like this:

<h1>Sign Up</h1>
<ng-form name='addUserForm'>
<div>First name: <input ng-model='user.first' required></div>
<div>Last name: <input ng-model='user.last' required></div>
<div>Email: <input type='email' ng-model='user.email' required></div>
<div>Age: <input type='number'
ng-model='user.age'
ng-maxlength='3'
ng-minlength='1'></div>
<div><button>Submit</button></div>
</ng-form>

Notice that we’re using the required attribute and input types for email and number
from HTML5 to do our validation on some of the fields. This works great with Angular,
and in older non-HTML5 browsers, Angular will polyfill these with directives that perform the same jobs.

We can then add a controller to this to handle the submission by changing the form to
reference it.

<ng-form name='addUserForm' ng-controller="AddUserController">

Inside the controller, we can access the validation state of the form through a property
called $valid. Angular will set this to true when all the inputs in the form are valid. We
can use this $valid property to do nifty things such as disabling the Submit button
when the form isn’t completed yet.

We can prevent form submission in an invalid state by adding ng-disabled to the
Submit button:

<button ng-disabled='!addUserForm.$valid'>Submit</button>

Finally, we might want the controller to tell the user she’s been successfully added. Our
final template would look like:

<h1>Sign Up</h1>
<form name='addUserForm' ng-controller="AddUserController">
<div ng-show='message'>{{message}}</div>
<div>First name: <input name='firstName' ng-model='user.first' required></div>
<div>Last name: <input ng-model='user.last' required></div>
<div>Email: <input type='email' ng-model='user.email' required></div>
<div>Age: <input type='number'
ng-model='user.age'
ng-maxlength='3'
ng-min='1'></div>
<div><button ng-click='addUser()'
ng-disabled='!addUserForm.$valid'>Submit</button>
</ng-form>

with controller:

function AddUserController($scope) {
$scope.message = '';
$scope.addUser = function () {
// TODO for the reader: actually save user to database...
$scope.message = 'Thanks, ' + $scope.user.first + ', we added you!';
};
}

Moving On

In the last two chapters, we looked at all the most commonly used features in the Angular framework. For each feature discussed, there are many additional details we have yet to cover. In the next chapter, we’ll get you going by examining a typical development workflow.

dsfsd

fsd

fsd

fsd

fsd

fs

d