Author Archives: admin

GAE: Google Cloud Storage

About Google Cloud Storage (GCS)

Google Cloud Storage is useful for storing and serving large files. Additionally, Cloud Storage offers the use of access control lists (ACLs), and the ability to resume upload operations if they’re interrupted, and many other features. (The GCS client library makes use of this resume capability automatically for your app, providing you with a robust way to stream data into GCS.)

About the Google Cloud Storage (GCS) client library

The GCS client library lets your application read files from and write files to buckets in Google Cloud Storage (GCS). This library supports reading and writing large amounts of data to GCS, with internal error handling and retries, so you don’t have to write your own code to do this. Moreover, it provides read buffering with prefetch so your app can be more efficient.

The GCS client library includes the following functionality:

Where to download the GCS client library

For download instructions and distribution contents, see the downloads page.

What you need to do to use the GCS client library

In order to access GCS from App Engine, you must activate a Cloud project for GCS as described on the activation page.

Alternative methods for accessing Google Cloud Storage

The GCS client library provides a way to read from and write to Google Cloud Storage that is closely integrated with Google App Engine, enabling App Engine apps to create objects in GCS and serve them from GCS.

However, there are other ways to access GCS from App Engine besides using the GCS client library. You can use any of these methods as well:

Blobstore API

You can use the Blobstore API to upload objects to and serve objects from GCS using the BlobStore API. You’ll need to use theBlobstoreService.createGsBlobKey() method to create a blob key representing the GCS object. This approach is useful for uploading files from a web page. When the Blobstore API is used together with the Images API, you get a powerful way to serve images, because you can serve images directly from GCS, bypassing the App Engine app, which saves on instance hour costs.

GCS REST API

You can use the Cloud Storage REST API directly to read and write data to GCS. The GCS client library actually uses the Cloud Storage REST API. However, the GCS REST API lacks the App Engine optimizations already done for you by the GCS client library, so you may be doing unnecessary work if you use the GCS REST API directly. If the GCS client library lacks some feature you need, and the REST API supplies that feature, using the REST API may be a good option.

GCS storage manager

If you need to upload objects quickly and don’t mind a manual process, you can use the GCS Storage Manager.

Key concepts of Google Cloud Storage

For complete details on GCS, incuding a complete description of concepts, you need to refer to the GCS documentation. The following brief synopsis of some GCS features impacting the GCS client library are provided as a convenience.

Buckets, objects, and ACLs

The storage location you read files from and write files to is a GCS bucket. GCS client library calls always specify the bucket being used. Your project can access multiple buckets. How do these buckets get created? There are no client library calls currently for creating GCS buckets, so you need to create these upfront using the Google Storage Manager or the gsutil tool provided by GCS.

Access to the buckets and to the objects contained in them is controlled by an access control list (ACL). Your Google Cloud project and your App Engine app are added to the ACL permitting bucket access during activation. The ACL governing bucket access is distinct from the potentially many ACLs governing the objects in that bucket. Thus, your app has read and write priviledges to the bucket(s) it is activated for, but it only has full rights to the objects it creates in the bucket. Your app’s access to objects created by other apps or persons is limited to the rights given your app by the objects’ creator.

If an object is created in the bucket without an ACL explicitly defined for it, it uses whatever default object ACL has been assigned to the bucket by the bucket owner. If the bucket owner has not specified a default object ACL, the object default is public-read, which means that anyone allowed bucket access can read the object.

ACLs and the GCS Client Library

An app using the GCS client library cannot change the bucket ACL, but it can specify an ACL that controls access to the objects it creates. The available ACL settings are described under documentation for the GcsService.FcsFileOptions object.

Modifying GCS objects

Once you have created an object in a bucket, it cannot be modified (no appending). To modify an object in a bucket, you need to overwrite the object with a new object of the same name that contains your desired changes.

GCS and “subdirectories”

Google Cloud Storage documentation refers to “subdirectories” and the GCS client library allows you to supply subdirectory delimiters when you create an object. However, GCS does not actually store the objects into any real subdirectory. Instead, the subdirectories are simply part of the object filename. For example, if I have a bucket my_bucket and store the file somewhere/over/the/rainbow.mp3, the file rainbow.mp3 is not really stored in the subdirectory somewhere/over/the/. It is actually a file named somewhere/over/the/rainbow.mp3.

Retries and exponential backoff

The GCS client library provides a configurable mechanism for automatic request retries in event of timeout failures when accessing GCS. The same mechanism also provides exponential backoff to determine an optimal processing rate. (For a description of exponential backoff in GCS, see the Google Cloud Storage documentation on backoff.)

To change the default values for retries and backoff, you use the RetryParams class.

Using GCS client library with the development app server

You can use the client library with the development server from SDK version 1.8.1 and greater. This provides GCS emulation using the local disk.

Pricing, quotas, and limits

There are no charges associated with making GCS client library calls to Google Cloud Storage.

However, any data stored at GCS is charged the usual GCS data storage fees. Cloud Storage is a pay-to-use service; you will be charged according to the Cloud Storage price sheet.

What to do next

To create, deploy, and run your app:

  1. Download the client library.
  2. Create an App Engine project and activate it for GCS.
  3. Optionally, if you have an existing app that uses the older Google Cloud Storage API, migrate your app.
  4. Go through the brief Getting Started instructions for a quick orientation in using the library.
  5. Upload and deploy your app to production App Engine.
  6. Test the app for expected behavior with GCS.

AngularJS: Yeoman

[Fuente : http://yeoman.io/]

YEOMAN 1.0

Introducción

Yeoman 1.0 es más que una herramienta. Es un workflow; una colección de herramientas y mejores prácticas para trabajar mejor cuando se programa web.

Nuestro workflow se compone de tres herramientas que mejoran tu productividad y satisfacción a la hora de desarrollar una web app:

  • Yo : el andamiaje de la nueva aplicación,  genera la configuración Grunt y tareas Grunt usuales que podrías necesitar para hacer los builds.
  • Grunt se utiliza para hacer los builds , previsualizar y probar tu proyecto.
  • Bower se utiliza para gestión de dependencias, de forma que nunca más tengas que manualmente descargarte y referenciar las librerias de javascript.

Estas  tres herramientas son programadas y mantenidas de forma separada , pero funcionan bien juntas como partes de un workflow efectivo.

Instalación

yo

Yo es mantenido por el proyecto Yeoman y ofrece el esqueleto de una aplicación web, utilizando templates a los que nos refereriremos como generadores. Tipicamente se instala yo y cualquier generador que creas que puedas usar utilizando npm.

Instalando yo y algunos generadores

Lo primero , necesitas instalar yo y otras herramientas necesarias:

npm install -g yo

If you are using npm 1.2.10 or above, this will also automatically install grunt and bower for you. If you’re on an older version of npm, you will need to install them manually:

# For npm versions < 1.2.10.
npm install -g grunt-cli bower

If you have installed Grunt globally in the past, you will need to remove it first: npm uninstall -g grunt

On Windows, we suggest you use an improved command line tool such as Console2 or PowerShell to improve the experience.

Andamiaje básico

Para hacer el andamiaje de una aplicación web, necesitas instalar el generator-webapp generator:

npm install -g generator-webapp

Este es el generador por defecto de aplicaciones web que te crea el esqueleto de un proyecto conteniendo HTML5 BoilerplatejQueryModernizr, and Bootstrap.Tendrás la elección durante la generación de incluir o no muchos de estos.

Ahora que el generador está instalado , crea un directorio para tu nuevo proyecto y ejecuta:

yo webapp

Cada proyecto que se crea con yo creará también tareas Grunt relevantes que la comunidad cree que son útiles para tu workflow.

El generador webapp se considera la forma más simple de empezar una web app. También proporcionamos framework generators que pueden ser utilizados para hacer el esqueleto de un proyecto con posibles vistas , modelos , controladores y demás.

Ejemplo : Creando el esqueleto de una AngularJS app

Como siempre , antes de utilizar un nuevo generador , asegurate de tener lo último , volviendo a instalarte el web.generator:

npm install -g generator-angular

Después de eso , crea un nuevo directorio para tu applicación , entonces ejecuta:

yo angular

Muchos generadores te permiten personalizar tu app utilizando flags en el comando inicial. Como ejemplo , con generator-angular, puedes poner:

yo angular --minsafe

Con esto estamos generando la estructura de ficheros para una aplicación básica y crea una serie de archivos para una nueva aplicación AngularJS encima de ello. Esto incluye patrones para directivas y controladores asi como esqueletos de Karma unit tests.

Haciendo el esqueleto de los componentes de tu aplicación AngularJS

Algunos generadores pueden ser utilizados para hacer el andamiaje de componentes de tu app – podemos llamarles sub-generadores.

En el framework AngularJS, por ejemplo , tu app se compone de una serie de piezas incluyendo controllers, directives y filtros. Puedes crear el esqueleto de todos estos componentes durante el workflow de desarrollo haciendo lo siguiente:

yo angular:controller myController
yo angular:directive myDirective
yo angular:filter myFilter
yo angular:service myService

Cada generador de framework tiene documentación disponible donde se puede consultar lo que soporta.

Crear tus propios generadores

See Generators.

Bower

Bower es un gestor de paquetes web que te permite fácilmente manejar las dependencias de tus proyectos (como lo que hace Maven con Java). Esto incluye recursos Javascript , imágenes y CSS. Es mantenido por Twitter y la comunidad open-source.

Manejar paquetes utilizando Bower puede ser hecho utilizando los siguientes comandos:

# Search for a dependency in the Bower registry.
bower search <dep>

# Install one or more dependencies.
bower install <dep>..<depN>

# List out the dependencies you have installed for a project.
bower list

# Update a dependency to the latest version available.
bower update <dep>

Using Bower with a project scaffolded using yo

To create a basic web app with a dependency on a jQuery plug-in:

# Scaffold a new application.
yo webapp

# Search Bower's registry for the plug-in we want.
bower search jquery-pjax

# Install it and save it to bower.json
bower install jquery-pjax --save

# If you're using RequireJS...
grunt bower
> Injects your Bower dependencies into your RequireJS configuration.

# If you're not using RequireJS...
grunt bower-install
> Injects your dependencies into your index.html file.

It’s as simple as that.

Your chosen generator may not include the grunt tasks “bower” and “bower-install”. You can read more about how to install and use these atgrunt-bower-requirejs and grunt-bower-install.

Grunt

Grunt es una herramienta de linea de comando para proyectos Javascript. Se puede utilizar para compilar proyectos , pero además tiene otros comandos que puedes utilizar en tu workflow.

Grunt commands

# Preview an app you have generated (with Livereload).
grunt server

# Run the unit tests for an app.
grunt test

# Build an optimized, production-ready version of your app.
grunt

These commands can be used together with the yo binary for a seamless development workflow:

yo webapp
grunt server
grunt test
grunt

AngularJS : NodeJS

[Fuente :

http://nodejs.org/

http://www.ibm.com/developerworks/ssa/opensource/library/os-nodejs/

]

Node.js es una plataforma construida sobre el Chrome’s JavaScript runtime para construir de forma fácil aplicaciones en la red que sean rápidas y escalables.Node.js utiliza un modelo orientado a eventos , no bloqueante del I/O que lo hace muy adecuado para aplicaciones en tiempo real que hacen uso intensivo de datos y que se ejecutan en dispositivos distribuidos.

El objetivo de Node es proporcionar una forma fácil de construir programas en red escalables

En el ejemplo “hola mundo” de web server que se muestra abajo , se pueden manejar muchas conexiones de clientes de forma concurrente. Node le dice al sistema operativo (a través de epoll , kqueue , /dev/poll o select) que debe ser notificado cuando se establezca una nueva conexión, y entonces volver al estado de sleep. Si alguien nuevo se conecta, entonces se ejecuta el callback. Cada conexión ocupa una pequeña pila de memoria.

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello Worldn');
}).listen(1337, "127.0.0.1");
console.log('Server running at http://127.0.0.1:1337/');

Esto contrasta con el modelo de concurrencia de hoy en día donde se utilizan los hilos del OS. Hacer networking basandose en los hilos del OS (Thread-based) es relativamente ineficiente y muy dificil de utilizar. Vease: this and this.Con Node se maneja la memoria de forma más eficiente cuando hay altas cargas de procesamiento que el sistema que suele reservar 2mb en la pila del thread por cada conexión.

Además, los usuarios de Node son libres de las preocupaciones de dead-locking en el proceso – no hay locks. Casi ninguna función en Node realiza directamente I/O, de forma que el proceso nunca se bloquea. Como no hay bloqueos , programadores no tan expertos pueden realizar desarrollos de forma rápida.

Node es similar en diseño y está influencia por sistema como Ruby’s Event Machine or Python’s Twisted. Node lleva el modelo de eventos un poco más lejos – presenta el loop de eventos como una construcción del lenguaje en vez de como una librería. En otros sistemas there is always a blocking call to start the event-loop. Typically one defines behavior through callbacks at the beginning of a script and at the end starts a server through a blocking call like EventMachine::run(). In Node there is no such start-the-event-loop call. Node simply enters the event loop after executing the input script. Node exits the event loop when there are no more callbacks to perform. This behavior is like browser javascript—the event loop is hidden from the user.

HTTP is a first class protocol in Node. Node’s HTTP library has grown out of the author’s experiences developing and working with web servers. For example, streaming data through most web frameworks is impossible. Node attempts to correct these problems in its HTTP parser and API. Coupled with Node’s purely evented infrastructure, it makes a good foundation for web libraries or frameworks.

But what about multiple-processor concurrency? Aren’t threads necessary to scale programs to multi-core computers? You can start new processes viachild_process.fork() these other processes will be scheduled in parallel. For load balancing incoming connections across multiple processes use the cluster module.

See also:

Un ejemplo : Webserver

This simple web server written in Node responds with “Hello World” for every request.

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello Worldn');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

To run the server, put the code into a file example.js and execute it with the node program from the command line:

% node example.js
Server running at http://127.0.0.1:1337/

Here is an example of a simple TCP server which listens on port 1337 and echoes whatever you send it:

var net = require('net');

var server = net.createServer(function (socket) {
  socket.write('Echo serverrn');
  socket.pipe(socket);
});

server.listen(1337, '127.0.0.1');

¿Simplemente qué es Node.js?

Resumen:  Node es un intérprete Javascript del lado del servidor que cambia la noción de cómo debería trabajar un servidor. Su meta es permitir a un programador construir aplicaciones altamente escalables y escribir código que maneje decenas de miles de conexiones simultáneas en una sola máquina física.

Introducción

Si usted ha oído acerca de Node, o si ha leído algún artículo destacando lo increíble que es, tal vez se esté preguntando “¿Simplemente qué es Node.js?”. Tal vez usted todavía tenga preguntas sobre lo que es Node, después de leer su propia página principal. Node definitivamente no es para todos los programadores, pero podría ser la respuesta adecuada para algunos.

Este artículo tratará de responder a los que es Node.js, presentando un breve contexto de los problemas que resuelve, cómo funciona, cómo ejecutar una aplicación simple y, finalmente, dónde Node es una buena solución. No tratará cómo escribir una aplicación Node complicada ni será un tutorial minucioso sobre Node. Leer este artículo le ayudará a decidir si debe continuar aprendiendo sobre Node para utilizarlo en su propio negocio.

¿Qué problema resuelve Node?

La meta número uno declarada de Node es “proporcionar una manera fácil para construir programas de red escalables”. ¿Cuál es el problema con los programas de servidor actuales? Hagamos cuentas. En lenguajes como Java™ y PHP, cada conexión genera un nuevo hilo que potencialmente viene acompañado de 2 MB de memoria. En un sistema que tiene 8 GB de RAM, esto da un número máximo teórico de conexiones concurrentes de cerca de 4.000 usuarios. A medida que crece su base de clientes, si usted desea que su aplicación soporte más usuarios, necesitará agregar más y más servidores. Desde luego, esto suma en cuanto a los costos de servidor del negocio, a los costos de tráfico, los costos laborales, y más. Además de estos costos están los costos por los problemas técnicos potenciales — un usuario puede estar usando diferentes servidores para cada solicitud, así que cualquier recurso compartido debe almacenarse en todos los servidores. Por todas estas razones, el cuello de botella en toda la arquitectura de aplicación Web (incluyendo el rendimiento del tráfico, la velocidad de procesador y la velocidad de memoria) era el número máximo de conexiones concurrentes que podía manejar un servidor.

Node resuelve este problema cambiando la forma en que se realiza una conexión con el servidor. En lugar de generar un nuevo hilo de OS para cada conexión (y de asignarle la memoria acompañante), cada conexión dispara una ejecución de evento dentro del proceso del motor de Node. Node también afirma que nunca se quedará en punto muerto, porque no se permiten bloqueos y porque no se bloquea directamente para llamados E/S. Node afirma que un servidor que lo ejecute puede soportar decenas de miles de conexiones concurrentes.

Entonces, ahora que usted tiene un programa que puede manejar cientos de miles de conexiones concurrentes, ¿qué puede usted construir en realidad con Node? Sería extraordinario si usted tuviera una aplicación Web que necesitara de toda esta cantidad de conexiones. Ese es uno de esos problemas del tipo “si usted tiene este problema, no es un problema”. Antes de pasar a ello, observemos cómo funciona Node y cómo está diseñado que se ejecute.

Lo que Node definitivamente no es

Sí, Node es un programa de servidor. Sin embargo, el producto base de Node definitivamente No es como Apache o Tomcat. Esos servidores básicamente son productos para servidor listos para instalar y que están listos para implementar aplicaciones instantáneamente. Usted podría tener un servidor estar listo y en operación en un minuto con estos productos. Node definitivamente no es esto. De forma similar a como Apache puede agregar un módulo PHP para permitir a los desarrolladores crear páginas Web dinámicas, y un módulo SSL para conexiones seguras, Node también tiene el concepto de módulos que se pueden agregar a su núcleo mismo. Literalmente hay cientos de módulos de los que se puede escoger con Node, y la comunidad es bastante activa en cuanto a producir, publicar y actualizar docenas de módulos por día. Hablaremos sobre toda la parte de módulos de Node más adelante en este artículo.

Cómo funciona Node

Node ejecuta V8 JavaScript. Espere… ¿qué? ¿JavaScript en el servidor? Sí, leyó correctamente. El JavaScript del lado del servidor puede ser un concepto nuevo para cualquiera que haya trabajado exclusivamente con JavaScript del lado del cliente, pero la idea en sí no es tan inverosímil — ¿por qué no utilizar el mismo lenguaje de programación que usted usa en el cliente del lado del servidor?

¿Qué es el V8? El motor V8 JavaScript es el motor JavaScript subyacente que Google usa con su navegador Chrome. Pocas personas piensan en lo que en realidad sucede con JavaScript en el cliente. Bien, un motor JavaScript en realidad interpreta el código y lo ejecuta. Con el V8, Google creó un intérprete ultra-rápido escrito en C++, con otro aspecto único: usted puede descargar el motor e incorporarlo a cualquier aplicación que desee. No está restringido a ejecutarse en un navegador. Así, Node en realidad usa el motor V8 JavaScript escrito por Google y le da otro propósito para usarlo en el servidor. ¡Perfecto! Para qué crear un nuevo lenguaje cuando ya hay una buena solución disponible.

Programación orientada por eventos

A muchos programadores se les ha hecho creer que la programación orientada a objetos es el diseño perfecto de programación y que no deben usar nada más. Node utiliza lo que se conoce como modelo de programación orientado por eventos.
Listado 1. Programación orientada por eventos del lado del cliente con jQuery

				
// jQuery code on the client-side showing how Event-Driven programming works

// When a button is pressed, an Event occurs - deal with it
// directly right here in an anonymous function, where all the
// necessary variables are present and can be referenced directly
$("#myButton").click(function(){
     if ($("#myTextField").val() != $(this).val())
         alert("Field must match button text");
});

El lado del servidor realmente no es tan diferente del lado del cliente. Es verdad, no se están presionando botones, y no se está ingresando texto en campos, pero a un nivel superior, están sucediendo eventos. Se realiza una conexión — ¡evento! Se reciben datos a través de la conexión — ¡evento! Se dejan de recibir datos por la conexión — ¡evento!

¿Por qué este tipo de configuración es ideal para Node? JavaScript es un gran lenguaje para programación orientada por eventos, porque permite funciones y cierres anónimos, y más importante, la sintaxis es similar para casi cualquier persona que haya codificado. las funciones de devolución de llamado que se llaman cuando ocurre un evento pueden escribirse en el mismo punto en el que usted captura el evento. Fácil de codificar, fácil de mantener. No hay infraestructuras complicadas Orientadas a Objeto, no hay interfaces, no hay potencial para sobre-arquitectura de nada. Simplemente esperar por un evento, escribir una función de devolución de llamado, ¡y se ha resuelto todo!

Aplicación Node de ejemplo

¡Finalmente vamos a ver algo de código! Reunamos todas las cosas sobre las que hemos tratado y creemos nuestra primera aplicación Node. Como hemos visto que Node es ideal para el manejo de aplicaciones de alto tráfico, creemos una aplicación Web bastante simple, construida para máxima velocidad. Estas son las especificaciones para nuestra aplicación de muestra pasadas por el “jefe”: Cree una API RESTful generadora de números aleatorios. La aplicación debe recibir una entrada, un parámetro llamado “number”. La aplicación retornará un número aleatorio que esté entre 0 y este parámetro, y retornará ese número generado a quien hizo el llamado. Y, como el “jefe” espera que esta sea una aplicación masivamente popular, deberá manejar 50.000 usuarios concurrentes. Observemos el código:
Listado 2. Generador Node de números aleatorios

				
// these modules need to be imported in order to use them.
// Node has several modules.  They are like any #include
// or import statement in other languages
var http = require("http");
var url = require("url");

// The most important line in any Node file.  This function
// does the actual process of creating the server.  Technically,
// Node tells the underlying operating system that whenever a
// connection is made, this particular callback function should be
// executed.  Since we're creating a web service with REST API,
// we want an HTTP server, which requires the http variable
// we created in the lines above.
// Finally, you can see that the callback method receives a 'request'
// and 'response' object automatically.  This should be familiar
// to any PHP or Java programmer.
http.createServer(function(request, response) {

     // The response needs to handle all the headers, and the return codes
     // These types of things are handled automatically in server programs
     // like Apache and Tomcat, but Node requires everything to be done yourself
     response.writeHead(200, {"Content-Type": "text/plain"});

     // Here is some unique-looking code.  This is how Node retrives
     // parameters passed in from client requests.  The url module
     // handles all these functions.  The parse function
     // deconstructs the URL, and places the query key-values in the
     // query object.  We can find the value for the "number" key
     // by referencing it directly - the beauty of JavaScript.
     var params = url.parse(request.url, true).query;
     var input = params.number;

     // These are the generic JavaScript methods that will create
     // our random number that gets passed back to the caller
     var numInput = new Number(input);
     var numOutput = new Number(Math.random() * numInput).toFixed(0);

     // Write the random number to response
     response.write(numOutput);

     // Node requires us to explicitly end this connection.  This is because
     // Node allows you to keep a connection open and pass data back and forth,
     // though that advanced topic isn't discussed in this article.
     response.end();

   // When we create the server, we have to explicitly connect the HTTP server to
   // a port.  Standard HTTP port is 80, so we'll connect it to that one.
}).listen(80);

// Output a String to the console once the server starts up, letting us know everything
// starts up correctly
console.log("Random Number Generator Running...");

Iniciando esta aplicación

Ponga el código anterior en un archivo llamado “random.js”. Ahora, para comenzar esta aplicación y ejecutarla (creando así el servidor HTTP y escuchando las conexiones en el puerto 80), simplemente corra el siguiente comando en su prompt de comandos: % node random.js. Así es como se verá cuando usted sepa que el servidor estará listo y en funcionamiento.

root@ubuntu:/home/moila/ws/mike# node random.js
Random Number Generator Running...

Accediendo a la aplicación

La aplicación está lista y funcionando. Node está escuchando a cualquier conexión en este momento, así que vamos a probar la aplicación. Como hemos creado una API RESTful simple, podemos accesar a la aplicación usando nuestro navegador Web. Digite la siguiente dirección (asegúrese de haber completado el paso anterior),

http://localhost/?number=27

La ventana de su navegador cambiará a un número aleatorio entre 0 y 27. Presione recargar en su navegador y obtendrá otro número aleatorio. Listo, ¡esta es su primera aplicación Node!

Node, ¿para qué sirve?

Entonces, después de leer todo sobre Node, usted tal vez pueda responder la pregunta “Simplemente qué es Node?” pero usted puede quedar con la duda sobre “¿En qué puedo utilizar Node?” Esa es una pregunta importante ya que hay algunas cosas para las que Node es realmente bueno.

Para qué sirve

Como ha visto hasta ahora, Node está extremadamente bien diseñado para situaciones en que usted esté esperando una gran cantidad de tráfico y donde la lógica del lado del servidor y el procesamiento requeridos, no sean necesariamente grandes antes de responder al cliente. Aquí hay algunos buenos ejemplos en donde Node haría un gran trabajo:

  • Una API RESTfulUn servicio Web que proporcione una API RESTful toma algunos parámetros, los interpreta, arma una respuesta y descarga esa respuesta (usualmente una cantidad relativamente pequeña de texto) de vuelta al usuario. Esta es una situación ideal para Node, porque puede construirse para que maneje decenas de miles de conexiones. Tampoco requiere una gran cantidad de lógica y básicamente sólo busca valores de una base de datos y los reúne como una respuesta. Como la respuesta es una pequeña cantidad de texto y la solicitud entrante es una pequeña cantidad de texto, el volumen de tráfico no es alto, y una máquina probablemente puede manejar las demandas de API de incluso la API de la más ocupada de las empresas.
  • Fila de TwitterPiense en una compañía como Twitter que recibe tweets y los escribe en una base de datos. Literalmente hay miles de tweets llegando cada segundo y la base de datos posiblemente no puede seguir el ritmo del número de escrituras necesarias durante los horarios pico de uso. Node se convierte en una pieza clave de la solución a este problema. Como hemos visto, Node puede manejar decenas de miles de tweets entrantes. Luego puede escribirlos rápida/fácilmente en un mecanismo de cola en memoria (memcached, por ejemplo), desde donde otro proceso separado puede escribirlos en la base de datos. El rol de Node en esto es reunir rápidamente el tweet y pasar esta información hacia otro proceso responsable de escribirlo. Imagine otro diseño — un servidor PHP normal que intente manejar escrituras en la base de datos misma — cada tweet podría causar una pequeña demora mientras se escribe en la base de datos, dado que el llamado de base de datos estaría bloqueando. Una máquina con este diseño sólo podría manejar 2.000 tweets entrantes por segundo, debido a la latencia de base de datos. A un millón de tweets por segundo, usted estaría hablando de 500 servidores. Node, en cambio, maneja cada conexión y no causa bloqueo, permitiéndole capturar tantos tweets como se le puedan arrojar. Una máquina nodo capaz de manejar 50.000 tweets por segundo, y usted estaría hablando de sólo 20 servidores.
  • Estadísticas de videojuegosSi usted alguna vez jugó un juego como Call of Duty on-line, algunas cosas le habrán llamado la atención inmediatamente cuando observó las estadísticas del juego, principalmente el hecho de que deben estar rastreando toneladas de información sobre el juego para poder producir tal nivel de estadísticas. Luego, multiplique esto por los millones de personas que lo juegan en cualquier momento, y tendrá una idea de la inmensa cantidad de información que se genera con bastante rapidez. Node es una buena solución para este escenario, porque puede capturar los datos que están generando los juegos, hacer un mínimo de consolidación con ellos y luego ponerlos en una fila para escribirlos en una base de datos. Parecería algo tonto dedicar todo un servidor a rastrear cuántas balas disparan las personas en los juegos, lo cual podría ser el límite útil si usted utilizara un servidor como Apache, pero parecería menos tonto si en lugar de ello usted pudiera dedicar un solo servidor a rastrear casi todas las estadísticas de un juego, como usted puede llegar a hacerlo con un servidor que ejecute Node.
Módulos Node

Aunque originalmente no era un tema planeado en el artículo, debido a la demanda popular, el artículo se ha expandido para incluir una breve introducción a los Node Modules y al Node Package Manager. Así como a lo que las personas se han acostumbrado al trabajar con Apache, usted puede expandir la funcionalidad de Node instalando módulos. No obstante, los módulos que usted puede utilizar con Node mejoran en gran medida el producto, tanto, que es poco probable que haya alguien que utilice Node sin instalar por lo menos algunos módulos. Así de importantes se han tornado los módulos, hasta el punto de convertirse en parte esencial del producto completo.

En el aparte de Recursos, incluyo un enlace a la página de módulos, donde están listados y disponibles todos los módulos posibles. Como una muestra rápida de las posibilidades, estos incluyen un módulo para escribir páginas creadas dinámicamente (como PHP), un módulo para facilitar el trabajo con MySQL, un módulo para ayudar con WebSockets, y un módulo para asistir en el análisis de texto y de parámetros, entre docenas de módulos disponibles. No entraré en detalles sobre los módulos, porque como mencioné, este sólo es un artículo de visión general que le ayuda a entender si Node es algo que usted debería buscar más adelante, pero es posible, si decide utilizarlo más adelante, que definitivamente también vaya a trabajar con los módulos disponibles.

Adicionalmente, Node presenta el Node Package Module, que es una forma integrada de instalar y administrar los módulos Node que esté usando. Este maneja automáticamente dependencias, de manera que usted puede estar seguro(a) de que cualquier módulo que usted desee instalar se instalará correctamente con todas sus partes necesarias. También sirve como una forma para publicar sus propios módulos en la comunidad Node, si usted opta por vincularse y escribir su propio módulo. Piense en el NPM como una forma fácil para expandir la funcionalidad de Node sin tener que preocuparse por desconfigurar su instalación Node. De nuevo, si usted opta por continuar avanzando en Node, el NPM será parte vital de su solución Node.

Nota del Editor

La versión inicialmente publicada de este artículo generó bastantes comentarios por parte de la comunidad, sobre los diferentes puntos que se presentaron. Desde ese entonces el autor ha revisado este artículo con esas ideas en mente. Este tipo de revisión y discusión por lo pares es parte vital del mundo de fuente abierta. Gracias a aquellos(as) que aportaron sus comentarios constructivos.

Como todos los proyectos de fuente abierta, Node.js continuará evolucionando y los desarrolladores descubrirán nuevos recursos y técnicas para superar cualquier número de limitaciones. Como siempre, alentamos a nuestros usuarios a que prueben la tecnología por sí mismos.

La pregunta que muchos de ustedes tenían al comienzo de este artículo “¿Simplemente qué es Node.js?” debería quedar respondida después de leerlo. Usted debe poder explicar en unas pocas frases sencillas y concisas lo que es Node.js. Si usted puede hacerlo, entonces estará un paso adelante de casi cualquier otro programador. Muchas personas con las que he hablado sobre Node han estado confundidas con respecto a lo que hace exactamente. Ellas están, y es comprensible, pensando en modo Apache — (— un servidor es una aplicación en la que usted descarga sus archivos HTML y todo funciona). Como la mayoría de los programadores están acostumbrados a Apache y a lo que hace, la forma más fácil de describir Node es comparándolo con Apache. Node es un programa que puede hacer todo lo que hace Apache (con algunos módulos), pero que también puede hacer mucho más, al ser una plataforma JavaScript extensible desde la cual usted puede construir.

En este artículo usted ha visto cómo Node cumple con sus metas de proporcionar servidores altamente escalables. Utiliza un motor JavaScript extremadamente rápido de Google, el motor V8. Utiliza un diseño Orientado por Eventos para mantener el código al mínimo y fácil de leer. Todos estos factores conducen a la meta deseada por Node — es relativamente fácil escribir una solución masivamente escalable.

Tan importante como entender lo que Node es, también es importante entender lo que Node no es. Node no es simplemente un reemplazo de Apache que instantáneamente vaya a hacer más escalable su aplicación Web PHP. Eso no podría estar más lejos de la verdad. Todavía es muy temprano en la vida de Node, pero está creciendo extremadamente rápido, la comunidad está involucrada muy activamente, se está creando una gran cantidad de módulos, y este producto en crecimiento podría estar en su negocio dentro de un año.

df
dfd
fd
fd
fdf
df
df
df
df
df
df
dfd
fd
fd
fd

Angular JS Book: Chapter 3: Organización de proyecto

[Fuente: Angular JS Ebook Chapter 3]

Te recomendamos iniciar tu proyecto con Yeoman, con el que te creas la estructura básica para iniciar una aplicación AngularJS.

Los ficheros de una aplicación se pueden clasificar en las siguientes categorías:

Ficheros fuente JS

Take a look at the app/scripts folder. This is where all your JS source code lives. One main file (app/scripts/app.js) will set up the the Angular module and the routes for your application.

In addition, there is a separate folder—app/scripts/controller—which houses the individual controllers. Controllers provide the action and publish data to the scope which will then be displayed in the view. Usually, they correspond one to one with the view.

Directives, filters, and services can also be found under app/scripts, either as complete files (directives.js, filters.js, services.js), or individually, if they are nice and complex.

HTML Angular template files

Now, every AngularJS partial template that Yeoman creates can be found in the app/views folder. This will mirror our app/scripts/controller folder for the most part.

There is one other important Angular template file, which is the main app/index.html. This is responsible for sourcing the AngularJS source files, as well as any source files you create for your application.If you end up creating a new JS file, ensure that you add it to the index.html, and also update the main module and the routes (Yeoman does this for you as well!).

JS library dependencies

Yeoman provides you the app/scripts/vendor folder for all JS source dependencies.Want to use Underscore or SocketIO in your application? No problem—add the dependency to the vendor folder (and your index.html!) and start referencing it in your application.

Static resources

You are creating an HTML application in the end, and it is a given that you will have CSS and image dependencies that you need served as part of your application.

The app/styles and app/img folders are for this very purpose. Just add what you need and start referring to them (with the correct relative paths, of course!) in your application.

Yeoman does not create the app/img path by default.

Unit tests

Testing is super important, and totally effortless when it comes to AngularJS. The
test/spec folder should mirror your app/scripts in terms of tests. Each file should
have a mirror spec file which has its unit tests. The seed creates a stub for each
controller file, under test/spec/controllers, with the same name as the original controller.
These are Jasmine-style specs, which describe a specification for each expected
behavior of the controller.

Integration tests

AngularJS comes with end-to-end testing support built right into the library. All
your E2E tests, in the form of Jasmine specs, are saved under the folder tests/e2e.
Yeoman does not create the tests/folder by default.

While the E2E tests might look like Jasmine, they are not. They are
functions that are executed asynchronously, in the future, by the
Angular Scenario Runner. So don’t expect to be able to do stuff like
you would in a normal Jasmine test (like console.log on the value
of a repeater).

There is also a simple HTML file generated that can be opened by itself in a browser
to run the tests manually. Yeoman doesn’t generate the stubs for these yet, but they
follow a similar style to the unit tests.

Configuration files

There are two configuration files needed. The first one, karma.conf.js, is generated
by Yeoman for you and is used to run the unit tests.

The second one, which Yeoman does not generate yet, is the karma.e2e.conf.js. This is used to run the scenario tests.

There is a sample file at the end of this chapter in the RequireJS integration section.
The config details the dependencies and the files to use when running the unit tests
using Karma. By default, it runs the Karma server at port 9876.

You might ask: how do I run my application? What about unit tests? How do I even
write these various pieces that you are talking about?

Don’t worry, young grasshopper, all in due time. In this chapter, we will deal with setting
up your project and development environment so that things can move along at a rapid
pace once we do start churning out some awesome code. What code you write, and how
it hooks together to form your final awesome application, will come in the next few
chapters.

Testing with AngularJS

We have said it before (even right in this chapter), and we will say it again: testing is
essential, and AngularJS makes it simple to write the right kind of unit and integration
tests. While AngularJS plays nicely with multiple test runners, we strongly believe that
Karma trumps most of them providing the most robust, solid, and insanely fast test
runner for all your needs.

Karma

Karma’s main reason for existence is to make your test-driven development (TDD)
workflow simple, fast, and fun. It uses NodeJS and SocketIO (you don’t need to know
what they are, just assume that they are awesome, cool libraries) to allow running your code, and tests in multiple browsers at insanely fast speeds. Go find out more at https://github.com/vojtajina/karma/.

TDD: An Intro

Test-driven development, or TDD, is an AGILE methodology that flips the development
lifecycle by ensuring that tests are written first, before the code is implemented, and that
tests drive the development (and are not just used as a validation tool).
The tenets of TDD are simple:

  • Code is written only when there is a failing test that requires the code to pass
  • The bare minimum amount of code is written to ensure that the test passes
  • Duplication is removed at every step
  • Once all tests are passing, the next failing test is added for the next required functionality.

These simple rules ensure that:

  • Your code develops organically, and that every line of code written is purposeful.
  • Your code remains highly modular, cohesive, and reusable (as you need to be able to test it).
  • You provide a comprehensive array of tests to prevent future breakages and bugs.
  • The tests also act as specification, and thus documentation, for future needs and changes.

We at AngularJS have found this to be true, and the entire AngularJS codebase has been developed using TDD. For an uncompiled, dynamic language like JavaScript, we strongly believe that having a good set of unit tests will reduce headaches in the future!
So how do we get this awesomeness that is Karma? Well, first ensure that NodeJS is
installed on your machine. This comes with NPM (Node Package Manager), which
makes it easy to manage and install the thousands of libraries available for NodeJS.
Once you have NodeJS and NPM installed, installing Karma is as easy as running:

sudo npm install -g karma

There you go. You are ready to start Karmaing (I just made that up, please don’t go about using it in real life) in three easy steps!

Getting your config file up

If you used Yeoman to create your app skeleton, then you already have a readymade
Karma config file waiting for you to use. If not, just go ahead and execute the
following command from the base folder of your application directory:

karma init

in your terminal console, and it will generate a dummy config file (karma.conf.js)
for you to edit to your liking, with some pretty standard defaults. You can use that.

Starting the Karma server

Just run the following command:

karma start [optionalPathToConfigFile]

This will start the Karma server on port 9876 (the default, which you can change
by editing the karma.conf.js file from the previous step). While Karma should open
up a browser and capture it automatically, it will print all the instructions needed
to capture another browser in the console. If you are too lazy to do that, just go to
http://localhost:9876 in another browser or device, and you are good to start running
tests in multiple browsers.While Karma can capture the usual browsers automatically, on start (Firefox, Chrome, IE, Opera, and even PhantomJS), it is not limited to just those browsers. Any device on which you can browse to a URL can possibly be a runner for Karma. So if you open up the browser of your iPhone or Android device and browse to http://machinename:9876 (provided it is accessible!), you could potentially run your tests on mobile devices as well.

Running the tests

Execute the following command:

karma run

That’s it. You should get your results printed right in the console where you ran the
command. Easy, isn’t it?
Unit Tests

AngularJS makes it easy to write your unit tests, and supports the Jasmine style of writing tests by default (as does Karma). Jasmine is what we call a behavior-driven development framework, which allows you to write specifications that denote how your code should behave. A sample test in Jasmine might look something like this.

describe("MyController:", function() {
	it("to work correctly", function() {
		var a = 12;
		var b = a;
		expect(a).toBe(b);
		expect(a).not.toBe(null);
	});
});

As you can see, it lends itself to a very readable format, as most of the code that could
be read in plain English. It also provides a very diverse and powerful set of matchers
(like the expect clauses), and of course has the xUnit staples of setUp and tearDowns
(functions that are executed before and after each individual test case).

AngularJS provides some nice mockups, as well as testing functions, to allow you to
create services, controllers, and filters right in your unit tests, as well as mock out
HttpRequests and the like. We will cover this in Chapter 5.

Karma can be integrated with your development workflow to make it easier, as well as
to get faster feedback on the code you have written.

Integration with IDEs
Karma does not have plug-ins (yet!) for all the latest and greatest IDEs, but you
don’t really need any. All you need to do is add a shortcut command to execute
“karma start” and “karma run” from within your IDE. This can usually be done by
adding a simple script to execute, or the actual shell command, depending on your
choice of editor. You should see the results every time it finishes running, of course.

Running tests on every change
This is utopia for many TDD developers: being able to run all their tests, every time
they press save, within a few milliseconds, and get results back quickly. And this
can be done with AngularJS + Karma pretty easily. Turns out, the Karma config file
(remember the karma.conf.js file from before?) has an innocuous-looking flag
named “autoWatch”. Setting it to true tells Karma to run your tests every time the
file it watches (which is your source and test code) changes. And if you do “karma
start” from within your IDE, guess what? The results from the Karma run will be
available right within your IDE. You won’t even need to switch to console or terminal
to figure out what broke!

d
s
sd
sd
sds
ds
dsd
sd
sd
sd
ds
d
ds
ds
ds
dd
sd
sds

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

AngularJS Tutorial

[Fuente: http://docs.angularjs.org/tutorial]

En este tutorial introducimos AngularJS haciendo una aplicación web de ejemplo. La app que haremos será un catálogo que muestra una lista de dispositivos Android, permite filtrar la lista y ver los detalles de un dispositivo.

Según avances por este tutorial:

  • Ver ejemplos de cómo utilizar client-side data binding y dependency injection para construir vistas dinámicas de datos que cambian inmediatamente en respuesta a acciones del usuario.
  • Ver cómo Angular crea listeners sobre tus datos sin la necesidad de programar manipulación DOM
  • Aprende de una forma fácil a testear tus aplicaciones web
  • Aprende cómo utilizar servicios Angular para hacer tareas web comunes, como recuperar datos para mostrarlo, de una forma fácil.
  • Y todo esto funciona en todos los navegadores!

Cuando finalices este tutorial serás capaz de:

  • Crear una aplicación dinámica que funcione en cualquier browser.
  • Definir las diferencias entre Angular y otros frameworks Javascript
  • Entender cómo funciona el data binding en AngularJS
  • Utilizar el angular-seed project como base para empezar proyectos nuevos
  • Crear y ejecutar test
  • Identificar recursos para aprender más sobre AngularJS

You can go through the whole tutorial in a couple of hours or you may want to spend a pleasant day really digging into it. If you’re looking for a shorter introduction to AngularJS, check out the Getting Started document.

Working with the code

You can follow this tutorial and hack on the code in either the Mac/Linux or the Windows environment. The tutorial relies on the use of Git versioning system for source code management. You don’t need to know anything about Git to follow the tutorial. Select one of the tabs below and follow the instructions for setting up your computer.

  1. You will need Node.js and Karma to run unit tests, so please verify that you have Node.js v0.8 or better installed and that the node executable is on your PATH by running the following command in a terminal window:
    1. node –version

    Additionally install Karma if you don’t have it already:

    1. npm install -g karma
  2. You’ll also need Git, which you can get from the Git site.
  3. Clone the angular-phonecat repository located at Github by running the following command:
    1. git clone git://github.com/angular/angular-phonecat.git

    This command creates the angular-phonecat directory in your current directory.

  4. Change your current directory to angular-phonecat:
    1. cd angular-phonecat

    The tutorial instructions assume you are running all commands from the angular-phonecat directory.

    You should run all git commands from Git bash.

    Other commands like test.bat or e2e-test.bat should be executed from the Windows command line.

  5. You need an http server running on your system, but if you don’t already have one already installed, you can use node to run scriptsweb-server.js, a simple bundled http server.

The last thing to do is to make sure your computer has a web browser and a good text editor installed. Now, let’s get some cool stuff done!

Get Started!

Introducción a AngularJS

[Fuente: http://angularjs.org/]

¿Porqué utilizar AngularJS?

El lenguaje HTML es bueno para declarar documentos estáticos, pero falla cuando intenta utuilizar vistas dinámicas en aplicaciones web. AngujarJS te permite extender el vocabulario HTML para tu aplicación. El entorno resultante es muy expresivo , legible y rápido de programar.

Alternativas

Existen otros frameworks que resuelven las carencias del HTML abstrayendo el HTML ,el CSS y/o el Javascript o proporcionando una forma imperativa de manipular el DOM.Ninguna de estas soluciones trata la raíz del problema: HTML no fue diseñado para vistas dinámicas.

Extensibilidad

AngularJS es un conjunto de herramientas para construirte el framework que más se ajuste a tu aplicación. Es completamente extensible y funciona bien con otras librerías. Cada aspecto puede ser modificado o ajustado a tus necesidades. Sigue leyendo para saber más.

Hola Mundo

Ejemplo 1

<!doctype html>
<html ng-app>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
  </head>
  <body>
    <div>
      <label>Name:</label>
      <input type="text" ng-model="yourName" placeholder="Enter a name here">
      <hr>
      <h1>Hello {{yourName}}!</h1>
    </div>
  </body>
</html>

Añadiendo algo de control

Data Binding

El Data-binding es la forma automática de actualizar la vista según el modelo cambia, así como actualizar el modelo cuando la vista cambia. Esto ahora mucha DOM manipulation.

Controller

Los controllers gestionan el comportamiento que hay detrás de los elementos DOM. AngularJS te permite expresar ese comportamiento de una forma legible sin que tenga que haber el código feo de manipular el DOM , registrar callbacks o listeners de cambios en el modelo.

Plain JavaScript

A diferencia de otros frameworks, no hay necesidad de heredar de tipos propietarios o envoilver el modelo en metodos getter. Tan solo se utiliza Javascript plano. Esto hace el código más fácil de testear, mantener, reutilizar y libre de código oscuro.

Ejemplo 2

index.html

<!doctype html>
<html ng-app>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
    <script src="todo.js"></script>
    <link rel="stylesheet" href="todo.css">
  </head>
  <body>
    <h2>Todo</h2>
    <div ng-controller="TodoCtrl">
      <span>{{remaining()}} of {{todos.length}} remaining</span>
      [ <a href="" ng-click="archive()">archive</a> ]
      <ul class="unstyled">
        <li ng-repeat="todo in todos">
          <input type="checkbox" ng-model="todo.done">
          <span class="done-{{todo.done}}">{{todo.text}}</span>
        </li>
      </ul>
      <form ng-submit="addTodo()">
        <input type="text" ng-model="todoText"  size="30"
               placeholder="add new todo here">
        <input class="btn-primary" type="submit" value="add">
      </form>
    </div>
  </body>
</html>

todo.js

function TodoCtrl($scope) {
  $scope.todos = [
    {text:'learn angular', done:true},
    {text:'build an angular app', done:false}];

  $scope.addTodo = function() {
    $scope.todos.push({text:$scope.todoText, done:false});
    $scope.todoText = '';
  };

  $scope.remaining = function() {
    var count = 0;
    angular.forEach($scope.todos, function(todo) {
      count += todo.done ? 0 : 1;
    });
    return count;
  };

  $scope.archive = function() {
    var oldTodos = $scope.todos;
    $scope.todos = [];
    angular.forEach(oldTodos, function(todo) {
      if (!todo.done) $scope.todos.push(todo);
    });
  };
}

todo.css

.done-true {
  text-decoration: line-through;
  color: grey;
}

Enganchando con servicios de Back

Deep Linking

Un deep link refleja donde está el usuario en tu app en cada momento, es útil para que los usuarios puedan tener en sus marcadores (o favoritos) accesos directos a links de tu app. Las aplicaciones con html estáticos hacen esto automáticamente , pero las aplicaciones AJAX por su naturaleza no lo hacen. AngularJS combina los beneficios de poder hacer deep link con la comodidad del comportamiento de una aplicación de escritorio.

Validación de formularios

La validación de formularios del lado del cliente es una parte importante para conseguir una  buena user experiencce. AngularJS te permite declarar las reglas de validación del formulario sin tener que escribir código Javascript.Escribes menos código , vete a tomarte la cervecita antes.

Comunicación con el servidor

AngularJS te proporciona una capa de gestionar servicios encima de XHR, lo cual simplifica dramáticamente el código. Se hace un wrap de XHR para darte manejo de excepciones y “promisas”. Las “promisas” simplifican bastante tu código gestionan el retorno asíncrono de los datos. Esto te permite asignar propiedades de forma síncrona aunque el retorno sea asíncrono.

Ejemplo 3
index.html

<!doctype html>
<html ng-app="project">
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular-resource.min.js">
    </script>
    <script src="https://cdn.firebase.com/v0/firebase.js"></script>
    <script src="http://firebase.github.io/angularFire/angularFire.js"></script>
    <script src="project.js"></script>
  </head>
  <body>
    <h2>JavaScript Projects</h2>
    <div ng-view></div>
  </body>
</html>

project.js

angular.module('project', ['firebase']).
  value('fbURL', 'https://angularjs-projects.firebaseio.com/').
  factory('Projects', function(angularFireCollection, fbURL) {
    return angularFireCollection(fbURL);
  }).
  config(function($routeProvider) {
    $routeProvider.
      when('/', {controller:ListCtrl, templateUrl:'list.html'}).
      when('/edit/:projectId', {controller:EditCtrl, templateUrl:'detail.html'}).
      when('/new', {controller:CreateCtrl, templateUrl:'detail.html'}).
      otherwise({redirectTo:'/'});
  });

function ListCtrl($scope, Projects) {
  $scope.projects = Projects;
}

function CreateCtrl($scope, $location, $timeout, Projects) {
  $scope.save = function() {
    Projects.add($scope.project, function() {
      $timeout(function() { $location.path('/'); });
    });
  }
}

function EditCtrl($scope, $location, $routeParams, angularFire, fbURL) {
  angularFire(fbURL + $routeParams.projectId, $scope, 'remote', {}).
  then(function() {
    $scope.project = angular.copy($scope.remote);
    $scope.project.$id = $routeParams.projectId;
    $scope.isClean = function() {
      return angular.equals($scope.remote, $scope.project);
    }
    $scope.destroy = function() {
      $scope.remote = null;
      $location.path('/');
    };
    $scope.save = function() {
      $scope.remote = angular.copy($scope.project);
      $location.path('/');
    };
  });
}

list.html

<input type="text" ng-model="search" class="search-query" placeholder="Search">
<table>
  <thead>
  <tr>
    <th>Project</th>
    <th>Description</th>
    <th><a href="#/new"><i class="icon-plus-sign"></i></a></th>
  </tr>
  </thead>
  <tbody>
  <tr ng-repeat="project in projects | filter:search | orderBy:'name'">
    <td><a href="{{project.site}}" target="_blank">{{project.name}}</a></td>
    <td>{{project.description}}</td>
    <td>
      <a href="#/edit/{{project.$id}}"><i class="icon-pencil"></i></a>
    </td>
  </tr>
  </tbody>
</table>

detail.html

<form name="myForm">
  <div class="control-group" ng-class="{error: myForm.name.$invalid}">
    <label>Name</label>
    <input type="text" name="name" ng-model="project.name" required>
    <span ng-show="myForm.name.$error.required" class="help-inline">
        Required</span>
  </div>

  <div class="control-group" ng-class="{error: myForm.site.$invalid}">
    <label>Website</label>
    <input type="url" name="site" ng-model="project.site" required>
    <span ng-show="myForm.site.$error.required" class="help-inline">
        Required</span>
    <span ng-show="myForm.site.$error.url" class="help-inline">
        Not a URL</span>
  </div>

  <label>Description</label>
  <textarea name="description" ng-model="project.description"></textarea>

  <br>
  <a href="#/" class="btn">Cancel</a>
  <button ng-click="save()" ng-disabled="isClean() || myForm.$invalid"
          class="btn btn-primary">Save</button>
  <button ng-click="destroy()"
          ng-show="project.$id" class="btn btn-danger">Delete</button>
</form>

Creando componentes

Directivas

Las directivas es una característica única y muy poderosa de Angular. Con las directivas podemos inventarnos nuevas tags de HTML que sean específicas a nuestra aplicación.

Componentes reutilizables

Utilizamos directivas también con el objetivo de hacer componentes reutilizables. Un componente te permite envolver y esconder un estructura DOM y CSS complejas y su comportamiento. Esto te permite enfocarte en lo que tu aplicación hace o qué aspecto tendrá de forma separada.

Localización

Una parte importante de las aplicaciones serias es la localización. En Angular con directivas y filtros puedes construir bloques que estén en todos los idiomas que quieras.

Ejemplo 4

index.html

<!doctype html>
<html ng-app="components">
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
    <script src="components.js"></script>
    <script src="beers.js"></script>
  </head>
  <body>
    <tabs>
      <pane title="Localization">
        Date: {{ '2012-04-01' | date:'fullDate' }} <br>
        Currency: {{ 123456 | currency }} <br>
        Number: {{ 98765.4321 | number }} <br>
      </pane>
      <pane title="Pluralization">
        <div ng-controller="BeerCounter">
          <div ng-repeat="beerCount in beers">
            <ng-pluralize count="beerCount" when="beerForms"></ng-pluralize>
          </div>
        </div>
      </pane>
    </tabs>
  </body>
</html>

components.js

angular.module('components', []).
  directive('tabs', function() {
    return {
      restrict: 'E',
      transclude: true,
      scope: {},
      controller: function($scope, $element) {
        var panes = $scope.panes = [];
 
        $scope.select = function(pane) {
          angular.forEach(panes, function(pane) {
            pane.selected = false;
          });
          pane.selected = true;
        }
 
        this.addPane = function(pane) {
          if (panes.length == 0) $scope.select(pane);
          panes.push(pane);
        }
      },
      template:
        '<div class="tabbable">' +
          '<ul class="nav nav-tabs">' +
            '<li ng-repeat="pane in panes" ng-class="{active:pane.selected}">'+
              '<a href="" ng-click="select(pane)">{{pane.title}}</a>' +
            '</li>' +
          '</ul>' +
          '<div class="tab-content" ng-transclude></div>' +
        '</div>',
      replace: true
    };
  }).
  directive('pane', function() {
    return {
      require: '^tabs',
      restrict: 'E',
      transclude: true,
      scope: { title: '@' },
      link: function(scope, element, attrs, tabsCtrl) {
        tabsCtrl.addPane(scope);
      },
      template:
        '<div class="tab-pane" ng-class="{active: selected}" ng-transclude>' +
        '</div>',
      replace: true
    };
  })

beers.js

function BeerCounter($scope, $locale) {
  $scope.beers = [0, 1, 2, 3, 4, 5, 6];
  if ($locale.id == 'en-us') {
    $scope.beerForms = {
      0: 'no beers',
      one: '{} beer',
      other: '{} beers'
    };
  } else {
    $scope.beerForms = {
      0: 'žiadne pivo',
      one: '{} pivo',
      few: '{} pivá',
      other: '{} pív'
    };
  }
}

Embed an inject

Embeddable (Anidable)

Angular JS se acopla bien con las otras tecnologías. Muchos otros frameworks requieren que te lo bajes completo. En la página fuente podemos ver viendo el código fuente que tiene varias aplicaciones Angular JS embebidas en ella.

Injectable

La inyección de dependencia en AngularJS te permite describir declarativamente cóomo tu aplicación estará ensamblada. Esto significa que tu aplicación no necesita ningún método main().

La inyección de dependencia es también el núcleo de AngularJS. Esto significa que cualquier componente que no se ajusta a tus necesidades puede ser fácilmente reemplazado.

Testable

AngularJS fue diseñado desde el principio para ser testeable. Fomenta la separación de la vista y del comportamiento, viene de serie con mocks, y se aprovecha completamente de la inyección de dependencia. Viene ya con un escenario de ejecución end-to-end incluido

 

Android : Implementación de un patrón de diseño Dashboard

[Fuente: http://www.javacodegeeks.com/2012/06/android-dashboard-design-pattern.html]

[audio http://wpcom.files.wordpress.com/2007/01/mattmullenweg-interview.mp3]

[youtube=http://www.youtube.com/watch?v=JaNH56Vpg-A&w=640&h=385]

[recently_posts]

En breve, diremos que un Dashboard es una página conteniendo símbolos claros y grandes para acceder a la funcionalidad principal y opcionalmente un área para información de noticias relevantes.

El principal objetivo de este artículo es implementar un patrón de diseño de Dashboard como el siguiente:

Paso 1: Crear el Title bar layout

Definiremos el layout de la title bar (header o cabecera) sólo una vez pero será requerida en varias pantallas. Mostraremos/ocultaremos el botón de Home y otros botones cuandoquiera que son necesitados. Una vez que has hecho el title bar layout, podemos utilizarlo el mismo layout en otros layouts utilizando ViewStub.

He aquí un ejemplo del  (header) xml layout:

header.xml

<?xml version="1.0" encoding="utf-8"?>
	<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	    android:layout_width="fill_parent"
	    android:layout_height="wrap_content"
	    android:background="@color/title_background" >

	    <LinearLayout
	        android:id="@+id/panelIconLeft"
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:layout_alignParentLeft="true"
	        android:layout_centerVertical="true"
	        android:layout_margin="5dp" >

	        <Button
	            android:id="@+id/btnHome"
	            android:layout_width="wrap_content"
	            android:layout_height="wrap_content"
	            android:background="@drawable/ic_home"
	            android:onClick="btnHomeClick" />
	    </LinearLayout>

	    <TextView
	        android:id="@+id/txtHeading"
	        style="@style/heading_text"
	        android:layout_width="fill_parent"
	        android:layout_height="wrap_content"
	        android:layout_centerInParent="true"
	        android:layout_marginLeft="5dp"
	        android:layout_marginRight="5dp"
	        android:layout_toLeftOf="@+id/panelIconRight"
	        android:layout_toRightOf="@id/panelIconLeft"
	        android:ellipsize="marquee"
	        android:focusable="true"
	        android:focusableInTouchMode="true"
	        android:gravity="center"
	        android:marqueeRepeatLimit="marquee_forever"
	        android:singleLine="true"
	        android:text=""
	        android:textColor="@android:color/white" />

	    <LinearLayout
	        android:id="@+id/panelIconRight"
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:layout_alignParentRight="true"
	        android:layout_centerVertical="true"
	        android:layout_margin="5dp" >

	        <Button
	            android:id="@+id/btnFeedback"
	            android:layout_width="wrap_content"
	            android:layout_height="wrap_content"
	            android:background="@drawable/ic_feedback"
	            android:onClick="btnFeedbackClick" />
	    </LinearLayout>

	</RelativeLayout>

El código de arriba hace referencias a estilos de styles.xml y dimensiones de dimen.xml:

styles.xml

<?xml version="1.0" encoding="utf-8"?>
	<resources>
	<style name="heading_text">
	        <item name="android:textColor">#ff000000</item>
	        <item name="android:textStyle">bold</item>
	        <item name="android:textSize">16sp</item>
	        <item name="android:padding">5dp</item>
	    </style>
	<style name="HomeButton">
	        <item name="android:layout_gravity">center_vertical</item>
	        <item name="android:layout_width">fill_parent</item>
	        <item name="android:layout_height">wrap_content</item>
	        <item name="android:layout_weight">1</item>
	        <item name="android:gravity">center_horizontal</item>
	        <item name="android:textSize">@dimen/text_size_medium</item>
	        <item name="android:textStyle">normal</item>
	        <item name="android:textColor">@color/foreground1</item>
	        <item name="android:background">@null</item>
	    </style>

	</resources>

dimen.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="title_height">45dip</dimen>
    <dimen name="text_size_small">14sp</dimen>
    <dimen name="text_size_medium">18sp</dimen>
    <dimen name="text_size_large">22sp</dimen>
</resources>

Paso 2: Crea una super (abstract) class

De hecho, en esta abstract super class, definiremos:

1) event handlers para los dos botones : Home and Feedback

2) Otros métodos

Los botones de Home y Feedback , que van a ser visibles en casi todas las activities y que requerirán las mismas acciones a ser realizadas (por ejemplo , llevar al usuario a la activity de Home). Así que en vez de escribir el mismo código en cada actividad, escribiremos un event handler sólo una vez en una clase abstracta que será superclase para todas las activities.

Puedes haberte dado cuenta en el fichero header.xml de donde están definidos los botones de Home y de Feedback:

android:onClick=”btnHomeClick” (Home button)

android:onClick=”btnFeedbackClick” (Feedback button)

, asi que definiremos este método una vez en la super clase (abstracta).

Por favor, buscate un ejemplo de ViewStub si nunca lo has utilizado.

Ahora , aquí está el código de la clase abstracta, la llamaremos DashboardActivity.java

package com.technotalkative.viewstubdemo;

	import android.app.Activity;
	import android.content.Intent;
	import android.os.Bundle;
	import android.view.View;
	import android.view.ViewStub;
	import android.widget.Button;
	import android.widget.TextView;

	public abstract class DashBoardActivity extends Activity {
	    /** Called when the activity is first created. */
	    @Override
	    public void onCreate(Bundle savedInstanceState) {
	        super.onCreate(savedInstanceState);
	    }

	    public void setHeader(String title, boolean btnHomeVisible, boolean btnFeedbackVisible)
	    {
	      ViewStub stub = (ViewStub) findViewById(R.id.vsHeader);
	      View inflated = stub.inflate();

	      TextView txtTitle = (TextView) inflated.findViewById(R.id.txtHeading);
	      txtTitle.setText(title);

	      Button btnHome = (Button) inflated.findViewById(R.id.btnHome);
	      if(!btnHomeVisible)
	       btnHome.setVisibility(View.INVISIBLE);

	      Button btnFeedback = (Button) inflated.findViewById(R.id.btnFeedback);
	      if(!btnFeedbackVisible)
	       btnFeedback.setVisibility(View.INVISIBLE);

	    }

	    /**
	     * Home button click handler
	     * @param v
	     */
	    public void btnHomeClick(View v)
	    {
	     Intent intent = new Intent(getApplicationContext(), HomeActivity.class);
	     intent.setFlags (Intent.FLAG_ACTIVITY_CLEAR_TOP);
	     startActivity(intent);

	    }

	    /**
	     * Feedback button click handler
	     * @param v
	     */
	    public void btnFeedbackClick(View v)
	    {
	     Intent intent = new Intent(getApplicationContext(), FeedbackActivity.class);
	     startActivity(intent);
	    }
	}

Step 3: Define Dashboard layout

01 <?xml version="1.0" encoding="utf-8"?>
02 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03     android:layout_width="fill_parent"
04     android:layout_height="fill_parent"
05     android:orientation="vertical" >
06
07     
08  
09
10     <ViewStub
11         android:id="@+id/vsHeader"
12         android:layout_width="fill_parent"
13         android:layout_height="wrap_content"
14         android:inflatedId="@+id/header"
15         android:layout="@layout/header" />
16
17     <LinearLayout
18         android:layout_width="fill_parent"
19         android:layout_height="wrap_content"
20         android:layout_weight="1"
21         android:orientation="vertical"
22         android:padding="6dip" >
23
24         <LinearLayout
25             android:layout_width="fill_parent"
26             android:layout_height="wrap_content"
27             android:layout_weight="1"
28             android:orientation="horizontal" >
29
30             <Button
31                 android:id="@+id/main_btn_eclair"
32                 style="@style/HomeButton"
33                 android:drawableTop="@drawable/android_eclair_logo"
34                 android:onClick="onButtonClicker"
35                 android:text="@string/EclairActivityTitle" />
36
37             <Button
38                 android:id="@+id/main_btn_froyo"
39                 style="@style/HomeButton"
40                 android:drawableTop="@drawable/android__logo_froyo"
41                 android:onClick="onButtonClicker"
42                 android:text="@string/FroyoActivityTitle" />
43         </LinearLayout>
44
45         <LinearLayout
46             android:layout_width="fill_parent"
47             android:layout_height="wrap_content"
48             android:layout_weight="1"
49             android:orientation="horizontal" >
50
51             <Button
52                 android:id="@+id/main_btn_gingerbread"
53                 style="@style/HomeButton"
54                 android:drawableTop="@drawable/android_gingerbread_logo"
55                 android:onClick="onButtonClicker"
56                 android:text="@string/GingerbreadActivityTitle" />
57
58             <Button
59                 android:id="@+id/main_btn_honeycomb"
60                 style="@style/HomeButton"
61                 android:drawableTop="@drawable/android_honeycomb_logo"
62                 android:onClick="onButtonClicker"
63                 android:text="@string/HoneycombActivityTitle" />
64         </LinearLayout>
65
66         <LinearLayout
67             android:layout_width="fill_parent"
68             android:layout_height="wrap_content"
69             android:layout_weight="1"
70             android:orientation="horizontal" >
71
72             <Button
73                 android:id="@+id/main_btn_ics"
74                 style="@style/HomeButton"
75                 android:drawableTop="@drawable/android_ics_logo"
76                 android:onClick="onButtonClicker"
77                 android:text="@string/ICSActivityTitle" />
78
79             <Button
80                 android:id="@+id/main_btn_jellybean"
81                 style="@style/HomeButton"
82                 android:drawableTop="@drawable/android_jellybean_logo"
83                 android:onClick="onButtonClicker"
84                 android:text="@string/JellyBeanActivityTitle" />
85         </LinearLayout>
86     </LinearLayout>
87 </LinearLayout>

Step 4: Define activity for handling this dashboard layout buttons click events.

In this activity, you will find the usage of setHeader() method to set the header for home activity, yes in this method i have passed “false” for home button because its already a home activity, but i have passed “true” for feedback button because feedback is needed to be visible. Other process are same as defining button click handlers.

01 package com.technotalkative.viewstubdemo;
02
03 import android.content.Intent;
04 import android.os.Bundle;
05 import android.view.View;
06
07 public class HomeActivity extends DashBoardActivity {
08     /** Called when the activity is first created. */
09     @Override
10     public void onCreate(Bundle savedInstanceState) {
11         super.onCreate(savedInstanceState);
12         setContentView(R.layout.main);
13         setHeader(getString(R.string.HomeActivityTitle), falsetrue);
14     }
15
16     /**
17      * Button click handler on Main activity
18      * @param v
19      */
20     public void onButtonClicker(View v)
21     {
22      Intent intent;
23
24      switch (v.getId()) {
25   case R.id.main_btn_eclair:
26    intent = new Intent(this, Activity_Eclair.class);
27    startActivity(intent);
28    break;
29
30   case R.id.main_btn_froyo:
31    intent = new Intent(this, Activity_Froyo.class);
32    startActivity(intent);
33    break;
34
35   case R.id.main_btn_gingerbread:
36    intent = new Intent(this, Activity_Gingerbread.class);
37    startActivity(intent);
38    break;
39
40   case R.id.main_btn_honeycomb:
41    intent = new Intent(this, Activity_Honeycomb.class);
42    startActivity(intent);
43    break;
44
45   case R.id.main_btn_ics:
46    intent = new Intent(this, Activity_ICS.class);
47    startActivity(intent);
48    break;
49
50   case R.id.main_btn_jellybean:
51    intent = new Intent(this, Activity_JellyBean.class);
52    startActivity(intent);
53    break;
54   default:
55    break;
56   }
57     }
58 }

Step 5: Define other activities and their UI layouts

Now, its time to define activities that we want to display based on the particular button click from dashboard. So define every activities and their layouts. Don’t forget to call setHeader() method wherever necessary.

Here is one example for such activity – Activity_Eclair.java

01 package com.technotalkative.viewstubdemo;
02
03 import android.os.Bundle;
04
05 public class Activity_Eclair extends DashBoardActivity {
06     /** Called when the activity is first created. */
07     @Override
08     public void onCreate(Bundle savedInstanceState) {
09         super.onCreate(savedInstanceState);
10         setContentView(R.layout.activity_eclair);
11         setHeader(getString(R.string.EclairActivityTitle), truetrue);
12     }
13 }

activity_eclair.xml

01 <?xml version="1.0" encoding="utf-8"?>
02 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03     android:layout_width="fill_parent"
04     android:layout_height="fill_parent"
05     android:orientation="vertical" >
06
07     
08  
09
10     <ViewStub
11         android:id="@+id/vsHeader"
12         android:layout_width="fill_parent"
13         android:layout_height="wrap_content"
14         android:inflatedId="@+id/header"
15         android:layout="@layout/header" />
16
17     <TextView
18         android:id="@+id/textView1"
19         android:layout_width="match_parent"
20         android:layout_height="match_parent"
21         android:gravity="center"
22         android:text="@string/EclairActivityTitle" />
23
24 </LinearLayout>

Step 6: Declare activities inside the AnroidManifest.xml file

Now you are DONE :)

Output:

Home screen _ Landscape
inner screen
inner screen

You can download source code from here: Android – Dashboard pattern implementation

Feedback/review are always welcome :)

Reference: Android – Dashboard design pattern implementation from our JCG partner Paresh N. Mayani at the TechnoTalkative blog.

PHP : Utilizando Google Calendar

[Fuente: http://framework.zend.com/manual/1.12/en/zend.gdata.calendar.html]

You can use the Zend_Gdata_Calendar class to view, create, update, and delete events in the online Google Calendar service.

See » http://code.google.com/apis/calendar/overview.html for more information about the Google Calendar API.

Conectando al Calendar Service

El Google Calendar API , como todos los los APIs GData, están basados en el Atom Publishing Protocol (APP), un formato desarrollado en XML para gestionar recursos web. El tráfico entre un cliente y los servidores de Google Calendar tiene lugar sobre HTTP y permite tanto conexiones autenticadas como no autenticadas.

Antes de que cualquier transacción ocurra , es necesario establecer una conexión. Crear una conexión a los servidores de Calendar incluye dos pasos:

  • Crear un cliente HTTP
  • Crear un instancia del servicio Zend_Gdata_Calendar y conectarlo a ese cliente

Autenticación

El Google Calendar API permite acceso a feeds de calendarios tanto públicos como privados:

  • Los feeds públicos no requieren autenticación, pero son de sólo lectura y ofrecen funcionalidad reducidad
  • Los feeds privados ofrecen una funcionalidad más completa pero requiere una conexión autenticada a los servidores de calendar. Hay tres esquemas de autenticación que son soportados por Google Calendar:
    • ClientAuth proporciona autenticación usuario/password directa a los servidores de calendar. Debido a que este esquema requiere que los usuarios proporcionen a tu aplicación su password , este tipo de autenticación es sólo recomendado cuando otros esquemas de autenticación son insuficientes.
    • AuthSub allows authentication to the calendar servers via a Google proxy server. This provides the same level of convenience as ClientAuth but without the security risk, making this an ideal choice for web-based applications.
    • MagicCookie allows authentication based on a semi-random URL available from within the Google Calendar interface. This is the simplest authentication scheme to implement, but requires that users manually retrieve their secure URL before they can authenticate, doesn’t provide access to calendar lists, and is limited to read-only access.

The Zend_Gdata library provides support for all three authentication schemes. The rest of this chapter will assume that you are familiar the authentication schemes available and how to create an appropriate authenticated connection. For more information, please see section the Authentication section of this manual or the » Authentication Overview in the Google Data API Developer’s Guide.

Creating A Service Instance

In order to interact with Google Calendar, this library provides the Zend_Gdata_Calendar service class. This class provides a common interface to the Google Data and Atom Publishing Protocol models and assists in marshaling requests to and from the calendar servers.

Once deciding on an authentication scheme, the next step is to create an instance of Zend_Gdata_Calendar. The class constructor takes an instance of Zend_Http_Client as a single argument. This provides an interface for AuthSub and ClientAuth authentication, as both of these require creation of a special authenticated HTTP client. If no arguments are provided, an unauthenticated instance ofZend_Http_Client will be automatically created.

The example below shows how to create a Calendar service class using ClientAuth authentication:

  1. // Parameters for ClientAuth authentication
  2. $service = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
  3. $user = “sample.user@gmail.com”;
  4. $pass = “pa$$w0rd”;
  5. // Create an authenticated HTTP client
  6. $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
  7. // Create an instance of the Calendar service
  8. $service = new Zend_Gdata_Calendar($client);

A Calendar service using AuthSub can be created in a similar, though slightly more lengthy fashion:

  1. /*
  2. * Retrieve the current URL so that the AuthSub server knows where to
  3. * redirect the user after authentication is complete.
  4. */
  5. function getCurrentUrl()
  6. {
  7.     global $_SERVER;
  8.     // Filter php_self to avoid a security vulnerability.
  9.     $php_request_uri =
  10.         htmlentities(substr($_SERVER[‘REQUEST_URI’],
  11.                             0,
  12.                             strcspn($_SERVER[‘REQUEST_URI’], “nr”)),
  13.                             ENT_QUOTES);
  14.     if (isset($_SERVER[‘HTTPS’]) &&
  15.         strtolower($_SERVER[‘HTTPS’]) == ‘on’) {
  16.         $protocol = ‘https://’;
  17.     } else {
  18.         $protocol = ‘http://’;
  19.     }
  20.     $host = $_SERVER[‘HTTP_HOST’];
  21.     if ($_SERVER[‘HTTP_PORT’] != ” &&
  22.         (($protocol == ‘http://’ && $_SERVER[‘HTTP_PORT’] != ’80’) ||
  23.         ($protocol == ‘https://’ && $_SERVER[‘HTTP_PORT’] != ‘443’))) {
  24.         $port = ‘:’ . $_SERVER[‘HTTP_PORT’];
  25.     } else {
  26.         $port = ”;
  27.     }
  28.     return $protocol . $host . $port . $php_request_uri;
  29. }
  30. /**
  31. * Obtain an AuthSub authenticated HTTP client, redirecting the user
  32. * to the AuthSub server to login if necessary.
  33. */
  34. function getAuthSubHttpClient()
  35. {
  36.     global $_SESSION, $_GET;
  37.     // if there is no AuthSub session or one-time token waiting for us,
  38.     // redirect the user to the AuthSub server to get one.
  39.     if (!isset($_SESSION[‘sessionToken’]) && !isset($_GET[‘token’])) {
  40.         // Parameters to give to AuthSub server
  41.         $next = getCurrentUrl();
  42.         $scope = “http://www.google.com/calendar/feeds/”;
  43.         $secure = false;
  44.         $session = true;
  45.         // Redirect the user to the AuthSub server to sign in
  46.         $authSubUrl = Zend_Gdata_AuthSub::getAuthSubTokenUri($next,
  47.                                                              $scope,
  48.                                                              $secure,
  49.                                                              $session);
  50.          header(“HTTP/1.0 307 Temporary redirect”);
  51.          header(“Location: ” . $authSubUrl);
  52.          exit();
  53.     }
  54.     // Convert an AuthSub one-time token into a session token if needed
  55.     if (!isset($_SESSION[‘sessionToken’]) && isset($_GET[‘token’])) {
  56.         $_SESSION[‘sessionToken’] =
  57.             Zend_Gdata_AuthSub::getAuthSubSessionToken($_GET[‘token’]);
  58.     }
  59.     // At this point we are authenticated via AuthSub and can obtain an
  60.     // authenticated HTTP client instance
  61.     // Create an authenticated HTTP client
  62.     $client = Zend_Gdata_AuthSub::getHttpClient($_SESSION[‘sessionToken’]);
  63.     return $client;
  64. }
  65. // -> Script execution begins here <-
  66. // Make sure that the user has a valid session, so we can record the
  67. // AuthSub session token once it is available.
  68. // Create an instance of the Calendar service, redirecting the user
  69. // to the AuthSub server if necessary.
  70. $service = new Zend_Gdata_Calendar(getAuthSubHttpClient());

Finally, an unauthenticated server can be created for use with either public feeds or MagicCookie authentication:

  1. // Create an instance of the Calendar service using an unauthenticated
  2. // HTTP client
  3. $service = new Zend_Gdata_Calendar();

Note that MagicCookie authentication is not supplied with the HTTP connection, but is instead specified along with the desired visibility when submitting queries. See the section on retrieving events below for an example.

Retrieving A Calendar List

The calendar service supports retrieving a list of calendars for the authenticated user. This is the same list of calendars which are displayed in the Google Calendar UI, except those marked as “hidden” are also available.

The calendar list is always private and must be accessed over an authenticated connection. It is not possible to retrieve another user’s calendar list and it cannot be accessed using MagicCookie authentication. Attempting to access a calendar list without holding appropriate credentials will fail and result in a 401 (Authentication Required) status code.

  1. $service = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
  2. $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
  3. $service = new Zend_Gdata_Calendar($client);
  4. try {
  5.     $listFeed= $service->getCalendarListFeed();
  6. } catch (Zend_Gdata_App_Exception $e) {
  7.     echo “Error: ” . $e->getMessage();
  8. }

Calling getCalendarListFeed() creates a new instance of Zend_Gdata_Calendar_ListFeed containing each available calendar as an instance of Zend_Gdata_Calendar_ListEntry. After retrieving the feed, you can use the iterator and accessors contained within the feed to inspect the enclosed calendars.

  1. echo “<h1>Calendar List Feed</h1>”;
  2. echo “<ul>”;
  3. foreach ($listFeed as $calendar) {
  4.     echo “<li>” . $calendar->title .
  5.          ” (Event Feed: ” . $calendar->id . “)</li>”;
  6. }
  7. echo “</ul>”;

Retrieving Events

Like the list of calendars, events are also retrieved using the Zend_Gdata_Calendar service class. The event list returned is of typeZend_Gdata_Calendar_EventFeed and contains each event as an instance of Zend_Gdata_Calendar_EventEntry. As before, the iterator and accessors contained within the event feed instance allow inspection of individual events.

Queries

When retrieving events using the Calendar API, specially constructed query URLs are used to describe what events should be returned. TheZend_Gdata_Calendar_EventQuery class simplifies this task by automatically constructing a query URL based on provided parameters. A full list of these parameters is available at the » Queries section of the Google Data APIs Protocol Reference. However, there are three parameters that are worth special attention:

  • User is used to specify the user whose calendar is being searched for, and is specified as an email address. If no user is provided, “default” will be used instead to indicate the currently authenticated user (if authenticated).
  • Visibility specifies whether a users public or private calendar should be searched. If using an unauthenticated session and no MagicCookie is available, only the public feed will be available.
  • Projection specifies how much data should be returned by the server and in what format. In most cases you will want to use the “full” projection. Also available is the “basic” projection, which places most meta-data into each event’s content field as human readable text, and the “composite” projection which includes complete text for any comments alongside each event. The “composite” view is often much larger than the “full” view.

Retrieving Events In Order Of Start Time

The example below illustrates the use of the Zend_Gdata_Query class and specifies the private visibility feed, which requires that an authenticated connection is available to the calendar servers. If a MagicCookie is being used for authentication, the visibility should be instead set to “private-magicCookieValue“, where magicCookieValue is the random string obtained when viewing the private XML address in the Google Calendar UI. Events are requested chronologically by start time and only events occurring in the future are returned.

  1. $query = $service->newEventQuery();
  2. $query->setUser(‘default’);
  3. // Set to $query->setVisibility(‘private-magicCookieValue’) if using
  4. // MagicCookie auth
  5. $query->setVisibility(‘private’);
  6. $query->setProjection(‘full’);
  7. $query->setOrderby(‘starttime’);
  8. $query->setFutureevents(‘true’);
  9. // Retrieve the event list from the calendar server
  10. try {
  11.     $eventFeed = $service->getCalendarEventFeed($query);
  12. } catch (Zend_Gdata_App_Exception $e) {
  13.     echo “Error: ” . $e->getMessage();
  14. }
  15. // Iterate through the list of events, outputting them as an HTML list
  16. echo “<ul>”;
  17. foreach ($eventFeed as $event) {
  18.     echo “<li>” . $event->title . ” (Event ID: ” . $event->id . “)</li>”;
  19. }
  20. echo “</ul>”;

Additional properties such as ID, author, when, event status, visibility, web content, and content, among others are available withinZend_Gdata_Calendar_EventEntry. Refer to the » Zend Framework API Documentation and the » Calendar Protocol Reference for a complete list.

Retrieving Events In A Specified Date Range

To print out all events within a certain range, for example from December 1, 2006 through December 15, 2007, add the following two lines to the previous sample. Take care to remove “$query->setFutureevents(‘true’)“, since futureevents will override startMin andstartMax.

  1. $query->setStartMin(‘2006-12-01’);
  2. $query->setStartMax(‘2006-12-16’);

Note that startMin is inclusive whereas startMax is exclusive. As a result, only events through 2006-12-15 23:59:59 will be returned.

Retrieving Events By Fulltext Query

To print out all events which contain a specific word, for example “dogfood”, use the setQuery() method when creating the query.

  1. $query->setQuery(“dogfood”);

Retrieving Individual Events

Individual events can be retrieved by specifying their event ID as part of the query. Instead of calling getCalendarEventFeed(),getCalendarEventEntry() should be called instead.

  1. $query = $service->newEventQuery();
  2. $query->setUser(‘default’);
  3. $query->setVisibility(‘private’);
  4. $query->setProjection(‘full’);
  5. $query->setEvent($eventId);
  6. try {
  7.     $event = $service->getCalendarEventEntry($query);
  8. } catch (Zend_Gdata_App_Exception $e) {
  9.     echo “Error: ” . $e->getMessage();
  10. }

In a similar fashion, if the event URL is known, it can be passed directly into getCalendarEntry() to retrieve a specific event. In this case, no query object is required since the event URL contains all the necessary information to retrieve the event.

  1. $eventURL = “http://www.google.com/calendar/feeds/default/private”
  2.           . “/full/g829on5sq4ag12se91d10uumko”;
  3. try {
  4.     $event = $service->getCalendarEventEntry($eventURL);
  5. } catch (Zend_Gdata_App_Exception $e) {
  6.     echo “Error: ” . $e->getMessage();
  7. }

Creating Events

Creating Single-Occurrence Events

Events are added to a calendar by creating an instance of Zend_Gdata_EventEntry and populating it with the appropriate data. The calendar service instance (Zend_Gdata_Calendar) is then used to used to transparently covert the event into XML and POST it to the calendar server. Creating events requires either an AuthSub or ClientAuth authenticated connection to the calendar server.

At a minimum, the following attributes should be set:

  • Title provides the headline that will appear above the event within the Google Calendar UI.
  • When indicates the duration of the event and, optionally, any reminders that are associated with it. See the next section for more information on this attribute.

Other useful attributes that may optionally set include:

  • Author provides information about the user who created the event.
  • Content provides additional information about the event which appears when the event details are requested from within Google Calendar.
  • EventStatus indicates whether the event is confirmed, tentative, or canceled.
  • Transparency indicates whether the event should be consume time on the user’s free/busy list.
  • WebContent allows links to external content to be provided within an event.
  • Where indicates the location of the event.
  • Visibility allows the event to be hidden from the public event lists.

For a complete list of event attributes, refer to the » Zend Framework API Documentation and the » Calendar Protocol Reference. Attributes that can contain multiple values, such as where, are implemented as arrays and need to be created accordingly. Be aware that all of these attributes require objects as parameters. Trying instead to populate them using strings or primitives will result in errors during conversion to XML.

Once the event has been populated, it can be uploaded to the calendar server by passing it as an argument to the calendar service’sinsertEvent() function.

  1. // Create a new entry using the calendar service’s magic factory method
  2. $event= $service->newEventEntry();
  3. // Populate the event with the desired information
  4. // Note that each attribute is crated as an instance of a matching class
  5. $event->title = $service->newTitle(“My Event”);
  6. $event->where = array($service->newWhere(“Mountain View, California”));
  7. $event->content =
  8.     $service->newContent(” This is my awesome event. RSVP required.”);
  9. // Set the date using RFC 3339 format.
  10. $startDate = “2008-01-20”;
  11. $startTime = “14:00”;
  12. $endDate = “2008-01-20”;
  13. $endTime = “16:00”;
  14. $tzOffset = “-08”;
  15. $when = $service->newWhen();
  16. $when->startTime = “{$startDate}T{$startTime}:00.000{$tzOffset}:00”;
  17. $when->endTime = “{$endDate}T{$endTime}:00.000{$tzOffset}:00”;
  18. $event->when = array($when);
  19. // Upload the event to the calendar server
  20. // A copy of the event as it is recorded on the server is returned
  21. $newEvent = $service->insertEvent($event);

Event Schedules and Reminders

An event’s starting time and duration are determined by the value of its when property, which contains the properties startTime, endTime, and valueString. StartTime and EndTime control the duration of the event, while the valueString property is currently unused.

All-day events can be scheduled by specifying only the date omitting the time when setting startTime and endTime. Likewise, zero-duration events can be specified by omitting the endTime. In all cases, date and time values should be provided in » RFC3339 format.

  1. // Schedule the event to occur on December 05, 2007 at 2 PM PST (UTC-8)
  2. // with a duration of one hour.
  3. $when = $service->newWhen();
  4. $when->startTime = “2007-12-05T14:00:00-08:00”;
  5. $when->endTime=”2007-12-05T15:00:00:00-08:00″;
  6. // Apply the when property to an event
  7. $event->when = array($when);

The when attribute also controls when reminders are sent to a user. Reminders are stored in an array and each event may have up to find reminders associated with it.

For a reminder to be valid, it needs to have two attributes set: method and a time. Method can accept one of the following strings: “alert”, “email”, or “sms”. The time should be entered as an integer and can be set with either the property minutes, hours, days, or absoluteTime. However, a valid request may only have one of these attributes set. If a mixed time is desired, convert to the most precise unit available. For example, 1 hour and 30 minutes should be entered as 90 minutes.

  1. // Create a new reminder object. It should be set to send an email
  2. // to the user 10 minutes beforehand.
  3. $reminder = $service->newReminder();
  4. $reminder->method = “email”;
  5. $reminder->minutes = “10”;
  6. // Apply the reminder to an existing event’s when property
  7. $when = $event->when[0];
  8. $when->reminders = array($reminder);

Creating Recurring Events

Recurring events are created the same way as single-occurrence events, except a recurrence attribute should be provided instead of a where attribute. The recurrence attribute should hold a string describing the event’s recurrence pattern using properties defined in the iCalendar standard (» RFC 2445).

Exceptions to the recurrence pattern will usually be specified by a distinct recurrenceException attribute. However, the iCalendar standard provides a secondary format for defining recurrences, and the possibility that either may be used must be accounted for.

Due to the complexity of parsing recurrence patterns, further information on this them is outside the scope of this document. However, more information can be found in the » Common Elements section of the Google Data APIs Developer Guide, as well as in RFC 2445.

  1. // Create a new entry using the calendar service’s magic factory method
  2. $event= $service->newEventEntry();
  3. // Populate the event with the desired information
  4. // Note that each attribute is crated as an instance of a matching class
  5. $event->title = $service->newTitle(“My Recurring Event”);
  6. $event->where = array($service->newWhere(“Palo Alto, California”));
  7. $event->content =
  8.     $service->newContent(‘ This is my other awesome event, ‘ .
  9.                          ‘ occurring all-day every Tuesday from ‘ .
  10.                          ‘2007-05-01 until 207-09-04. No RSVP required.’);
  11. // Set the duration and frequency by specifying a recurrence pattern.
  12. $recurrence = “DTSTART;VALUE=DATE:20070501rn” .
  13.         “DTEND;VALUE=DATE:20070502rn” .
  14.         “RRULE:FREQ=WEEKLY;BYDAY=Tu;UNTIL=20070904rn”;
  15. $event->recurrence = $service->newRecurrence($recurrence);
  16. // Upload the event to the calendar server
  17. // A copy of the event as it is recorded on the server is returned
  18. $newEvent = $service->insertEvent($event);

Using QuickAdd

QuickAdd is a feature which allows events to be created using free-form text entry. For example, the string “Dinner at Joe’s Diner on Thursday” would create an event with the title “Dinner”, location “Joe’s Diner”, and date “Thursday”. To take advantage of QuickAdd, create a new QuickAdd property set to TRUE and store the freeform text as a content property.

  1. // Create a new entry using the calendar service’s magic factory method
  2. $event= $service->newEventEntry();
  3. // Populate the event with the desired information
  4. $event->content= $service->newContent(“Dinner at Joe’s Diner on Thursday”);
  5. $event->quickAdd = $service->newQuickAdd(“true”);
  6. // Upload the event to the calendar server
  7. // A copy of the event as it is recorded on the server is returned
  8. $newEvent = $service->insertEvent($event);

Modifying Events

Once an instance of an event has been obtained, the event’s attributes can be locally modified in the same way as when creating an event. Once all modifications are complete, calling the event’s save() method will upload the changes to the calendar server and return a copy of the event as it was created on the server.

In the event another user has modified the event since the local copy was retrieved, save() will fail and the server will return a 409 (Conflict) status code. To resolve this a fresh copy of the event must be retrieved from the server before attempting to resubmit any modifications.

  1. // Get the first event in the user’s event list
  2. $event = $eventFeed[0];
  3. // Change the title to a new value
  4. $event->title = $service->newTitle(“Woof!”);
  5. // Upload the changes to the server
  6. try {
  7.     $event->save();
  8. } catch (Zend_Gdata_App_Exception $e) {
  9.     echo “Error: ” . $e->getMessage();
  10. }

Deleting Events

Calendar events can be deleted either by calling the calendar service’s delete() method and providing the edit URL of an event or by calling an existing event’s own delete() method.

In either case, the deleted event will still show up on a user’s private event feed if an updateMin query parameter is provided. Deleted events can be distinguished from regular events because they will have their eventStatus property set to “http://schemas.google.com/g/2005#event.canceled”.

  1. // Option 1: Events can be deleted directly
  2. $event->delete();
  1. // Option 2: Events can be deleted supplying the edit URL of the event
  2. // to the calendar service, if known
  3. $service->delete($event->getEditLink()->href);

Accessing Event Comments

When using the full event view, comments are not directly stored within an entry. Instead, each event contains a URL to its associated comment feed which must be manually requested.

Working with comments is fundamentally similar to working with events, with the only significant difference being that a different feed and event class should be used and that the additional meta-data for events such as where and when does not exist for comments. Specifically, the comment’s author is stored in the author property, and the comment text is stored in the content property.

  1. // Extract the comment URL from the first event in a user’s feed list
  2. $event = $eventFeed[0];
  3. $commentUrl = $event->comments->feedLink->url;
  4. // Retrieve the comment list for the event
  5. try {
  6. $commentFeed = $service->getFeed($commentUrl);
  7. } catch (Zend_Gdata_App_Exception $e) {
  8.     echo “Error: ” . $e->getMessage();
  9. }
  10. // Output each comment as an HTML list
  11. echo “<ul>”;
  12. foreach ($commentFeed as $comment) {
  13.     echo “<li><em>Comment By: ” . $comment->author->name “</em><br/>” .
  14.          $comment->content . “</li>”;
  15. }
  16. echo “</ul>”;

Google APIs: cómo utilizar OAuth 2.0 para acceder

[Fuente: https://developers.google.com/accounts/docs/OAuth2?hl=es#scenarios]

Las APIs de Google utilizan el OAuth 2.0 protocol para autenticación y autorización. Goolgle dispone de varios flujos OAuth 2.0 que cubren todos los escenarios típicos, véase : web server , Javascript , dispositivo , aplicación instalada y servidor – servidor.

OAuth 2.0 es un protocolo relativamente simple y un programador puede integrar con Google’s OAuth 2.0 endpoints sin demasiado esfuerzo. Como pincelada introductoria, hay que registrar la aplicación con Google , redirigir el navegador a una URL , parsear el token de la respuesta , y enviar el token al servicio de Google API que desees.

Este artículo es una introducción a los escenarios OAuth 2.0 que Google soporta y proporciona enlaces a contenidos con más información.

Dadas las implicaciones de seguridad de acceder a la implementación correcta , recomendamos encarecidamente a los programadores utilizar las librerías OAuth 2.0 cuando interaccionan con Google’s OAuth 2.0 endpoints (ver Client libraries pars más info). Proximamente , más características serán añadidas a estas librerías.

Contents

  1. Basic Steps
  2. Simple Example
  3. Scenarios
    1. Login
    2. Web Server Applications
    3. Client-side Applications
    4. Installed Applications
    5. Devices
    6. Service Accounts
  4. Client Libraries

Pasos Básicos

Las aplicaciones siguen el mismo patrón básico cuando acceden a cualquier API de Google utilizando OAuth 2.0. A alto nivel, siempre se dan los siguientes 4 pasos:

1. Registrar la aplicación

Todas las aplicaciones que accedan al Google API deben estar registradas a través de la APIs Console.El resultado de este proceso de registro es un conjunto de valores que son conocidos tanto por Google como por tu aplicación (es decir , el client-id , el client-secret , los Javascript origins , la redirect-uri, etc). Por ejemplo: una aplicación Javascript no requiere un secret, pero una aplicación en servidor web sí.

2. Obtener el Token de acceso desde un Google Authorization Server

Antes de que tu aplicación pueda acceder a Google API , debe obtener un token de acceso que garantice el acceso al API. Un solo token de acceso puede autorizarte varios grados de acceso a multiples APIs. El conjunto de recursos y poeraciones permitidos por un token de acceso se controla durante la petición del token de acceso via una variable parámetro llamada ‘scope‘. Varios scopes pueden ser solicitados en una petición.

Hay varias formas de hacer una petición, y varia en función del tipo de aplicación que se está desarrollando. Por ejemplo: una aplicación Javascript puede solicitar un token de acceso utilizando un browser redirect a Google , mientras que una aplicación instalada en un dispositivo que no tiene browser utiliza peticiones web service.

La petición requiere que el usuario se logue en Google. Después de logarse , el usuario verá el permiso solicitado por la aplicación y será preguntado si quiere autorizar esos permisos a esa aplicación. Este proceso se llama “consentimiento del usuario“.

Si el usuario autoriza esos permisos a tu aplicación , a tu aplicación le será enviado un token de acceso o un código de autorización (el cual es utilizado para obtener un token de acceso). Si el usuario no autoriza permisos a tu aplicación , el Google Authorization Server retorna un error.

3. Enviar el Token de Acceso a un API

Después de que una aplicación ha obtenido el token de acceso, puede enviar el token de acceso en una petición a cualquier API de Google. Los tokens de acceso son válidos sólo para un conjunto de operaciones y recursos descritos en la petición del token. Por ejemplo:if an access token is issued for the Google+ API, it will not grant access to the Google Contacts API. It may, however, be sent to the Google+ API multiple times for similar operations.

Los tokens de acceso son enviados al API de Google en la HTTP Authorization header,o como un parámetro de la querystring (if HTTP header operations are not available).

4. Refresca el Token de Acceso  (opcional)

Los tokens de acceso tiene un tiempo de vida limitado, en algunos casos, una aplicación necesita acceso al Google API más allá del tiempo de un solo token de acceso. Cuando éste es el caso, tu aplicación puede obtener lo que se llama un token de refresco. Un token de refresco te permite obtener nuevos token de acceso.

Note that there are limits on the number of refresh tokens that will be issued; one limit per client/user combination, and another per user across all clients. You should save refresh tokens in long-term storage and continue to use them as long as they remain valid. If your application requests too many refresh tokens, it may run into these limits, in which case older refresh tokens will stop working.

Ejemplo simple

A continuación un ejemplo trivial de cómo utilizar Google’s OAuth 2.0 endpoint para obtener acceso al API de Google. It’s a Python web application running on App Engine. The flow of the example is fairly straightforward:

  1. Cuando la aplicación se carga , muestra al usuario el enlace “Login”
  2. Cuando el usuario hace click en Login, le será solicitado el login en Google y le pedirá que dé autorización de información básica  de la cuenta a la aplicación (consentimiento del usuario)
  3. Si el usuario concede los permisos, la aplicación recibe un token de acceso
  4. Una vez que tiene el token de acceso, la aplicación presenta el token de acceo al Google API que proporciona la información básica de la cuenta.(https://www.googleapis.com/oauth2/v1/userinfo)
  5. La aplicación renderiza le información básica de la cuenta en una tabla simple

Try it out for yourself!

Scenarios

Login

El login de usuario es siempre una parte esencial para acceder a la mayoría de las apis de Google. Tu aplicación puede utilizar el sistema de autenticación de Google para delegar la autenticación y obtención del perfil a Google.

La secuencia de login comienza redireccionando el navegador (popup , o página web normal si es necesario) a una url de Google con un conjunto de parámetros en la query string. Será Google el que se encargue de seleccionar la sesión correcta (el usuario puede haberse logado previamente con múltiples identidades) , aceptar y validar las credenciales del usuario y el one-time-password (si la cuenta lo requiere) , obteniendo consentimiento para proporcionar información básica del perfil, así como retornar un token de acceso OAuth 2.0 a tu aplicación.

El resultado de la secuencia de autenticación del usuario es un token de acceso OAuth 2.0, con el que ya puedes obtener información del perfil del usuario invocando al UserInfo Google API.

La información retornada del servicio de UserInfo puede ser utilizada durante el registro del usuario lo que minimiza los datos que el usuario tiene que introducir en el registro.

Además está el beneficio añadido de que tu site no tiene que mantener usuarios / contraseña en un almacenamiento seguro.

Para más información Login documentation.

Web Server Applications

Los servidores de Autorización OAuth2.0 de Google soportan aplicaciones web de servidor (es decir PHP , Java , Python , Ruby, ASP.NET,etc) . Esta secuencia comienza redireccionando el navegador (popup , o página completa si es necesario) a un url de Google con un conjunto de parámetros en la query string que indican el API y el tipo de acceso que nuestra aplicación solicita. Como en otros escenarios, Google se encarga de autenticar al usuario, selección de la sesiones que tenga ya guardadas el usuario y del consentimiento, pero el resultado de la secuencia en este caso es un “authorization code”. Después de recibir el código de autorización , la aplicación puede intercambiar el código por un código de acceso y un refresh token.

La aplicación puede acceder al Google API después de que se reciba el token de acceso.

Para más información -> Web Server documentation.

Client-side Applications

The Google OAuth 2.0 Authorization Server supports JavaScript applications (JavaScript running in a browser). Like the other scenarios, this one begins by redirecting a browser (popup, or full-page if needed) to a Google URL with a set of query string parameters that indicate the type of Google API access the application requires. Google handles the user authentication, session selection, and user consent. The result is an access token. The client should then validate the token. After validation, the client includes the access token in a Google API request.

For more information, see the Client-side documentation.

Installed Application

The Google OAuth 2.0 Authorization Server supports desktop and mobile applications (e.g. Android, Windows, Mac OS, iOS, Blackberry, etc.). These applications, in general, cannot keep secrets.

The sequence for installed applications is similar to the one shown in the Web Server section, but there are three exceptions:

  1. When registering the application, you specify that the application is an Installed application. This results in a different value for the redirect_uri parameter.
  2. The client_id and client_secret obtained during registration are embedded in the source code of your application. In this context, the client_secret is obviously not treated as a secret.
  3. The authorization code is returned to your application differently.

This sequence begins by redirecting a browser (either a browser embedded in the application or the system browser) to a Google URL with a set of query parameters that indicate the type of Google API access the application requires. Like other scenarios, Google handles the user authentication, session selection, and user consent. The result of the sequence is an authorization code. Your application can choose to have the authorization code returned in the title of the web page or to a http://localhost port. Once the application receives the authorization code, it can exchange the code for an access token and a refresh token.

After the application has received the access and refresh tokens, it may store the refresh token for future use, and use the access token to access a Google API. Once the access token expires, the application obtains a new one with the refresh token.

For more information, see the Installed Application documentation.

Devices

The Google OAuth 2.0 Authorization Server supports applications that run on devices with limited input capabilities (e.g. game consoles, video cameras, printers). In these cases, the user must have separate access to a computer or device with richer input capabilities. The user will first interact with application on the limited device, obtain an URL and a code from the device, then switch to a device or computer with richer input capabilities and launch a browser. Once in a browser, the user will navigate to the URL specified on the device, authenticate, and enter the code.

The sequence begins with the application making a request to a Google URL for a new code. The response contains several parameters, including the URL and code that should be shown to the user. The application should present these values to the user, and begin polling a Google URL at a specified interval. The response to a message in this polling sequence indicates whether or not the user has approved access. After the user approves access (via another computer or device), the response contains an access and refresh token.

After the application has received the access and refresh tokens, it may store the refresh token for future use, and use the access token to access a Google API. Once the access token expires, the application obtains a new one with the refresh token.

For more information, see the Device documentation.

Cuentas de Servicio (Service Accounts)

Varios APIs de Google actuan desde el lado de una aplicación y no acceden a información del usuario. Ejemplos de estos APIs son por ejemplo el API de Predicciones y el Google Cloud Storage.Cuando una aplicación accede a Google Cloud Storage, la aplicación necesita probar su propia identidad antes de realizar operaciones en la nube así que necesita obtener la aprobación del usuario.

Hay también una opción para que una aplicación pueda solicitar acceso delegado a un recurso en entornos de enterprise. El Google’s OAuth 2.0 Authorization Server soporta estos tipos de aplicaciones, y esta sección describe cómo una aplicación puede probar su identidad antes de acceso a un Google API compatible.

El mecanismo de esta interacción requiere que las aplicaciones se creen de forma criptográfica unos JSON Web Tokens (JWTs). Se recomienda también en este caso a los programadores que para esto utilicen un librería. Escribir este código sin el uso de una librería que abstraiga la creación de token y el firmado es muy dado a errores que pueden tener un impacto severo en la seguridad de tus aplicaciones. Para una lista librerias que soportan  este flujo , ver  OAuth 2.0 Service Accounts documentation

Esta secuencia comienza con la creación de una Service Account. Puedes crear una en la Google APIs console, o si estás utilizando Google App Engine, se crea una automáticamente cuando arrancas la aplicación GAE. Durante la creación de la Service Account en la Google APIs Console se te preguntará para descargarte una private key. Estate seguro de guardar esta private key en un lugar seguro. After the Service Account has been created, you will also have access to the client ID associated with the private key. You will need both when coding your application.

After obtaining the client ID and private key from the Google APIs Console, create a JWT and sign it with the private key, and construct an access token request in the appropriate format. Your application then sends the token request to the Google OAuth 2.0 Authorization Server and an access token will be returned. The application can access the API only after receiving the access token. When the access token expires, the application repeats the process.

For more information, see the Service Account documentation.

Client libraries

The following client libraries make implementing OAuth 2.0 even simpler by integrating with popular frameworks: