Resolviendo el error 403 cuando añadimos elementos por medio de API REST en SharePoint 2013

Hola a todos,

Hoy os quiero mostrar en una breve entrada cómo resolver el error 403 que surge cuando hacemos una petición POST para añadir elementos en una lista a través de la API REST de SharePoint 2013.

En mi caso, estoy trabajando en un proyecto en el que utilizo SharePoint y AngularJS, por eso, los ejemplos de las peticiones que os voy a mostrar, son usando el módulo $http de AngularJS.

Cuando hacemos una petición como esta:

$http({
            url: urlBase,
            method: "POST",
            data: JSON.stringify(likeJSON),
            headers: {
                "accept": "application/json;odata=verbose",
                "content-type": "application/json;odata=verbose",
            }
        });

El resultado que nos devuelve es un error como éste:

“The security validation for this page is invalid and might be corrupted. Please use your web browser’s Back button to try your operation again.”

Para solucionarlo, debemos añadir en la cabecera de la petición el parámetro X-RequestDigest como sigue:

$http({
            url: urlBase,
            method: "POST",
            data: JSON.stringify(likeJSON),
            headers: {
                "accept": "application/json;odata=verbose",
                "content-type": "application/json;odata=verbose",
                "X-RequestDigest": requestDigest,
            }
        });

Esta cabecera contiene información conocida como form digest. Ésta es un objeto que se inserta en una página por SharePoint y es usado para validar las peticiones de cliente

La pregunta ahora es: ¿Cómo conseguimos ese valor?. Pues lo podemos obtener por medio de la siguiente petición:

$http({
        url: "/_api/contextinfo",
        method: "POST",
        headers: {
            "accept": "application/json;odata=verbose",
            "content-type": "application/json;odata=verbose"
        }
    }).then(function (response) {
        requestDigest = response.data.d.GetContextWebInformation.FormDigestValue;
    },
    function () {

    });

Por medio de una petición POST al servicio REST con el EndPoint en la url /_api/contextinfo

De esta forma conseguimos resolver el error 403 que nos devuelven las peticiones POST al servicio REST en SharePoint.

Os dejo un enlace que me ha servido de guía, y en el que me he basado.

http://blogs.msdn.com/b/nadeemis/archive/2012/10/23/tip-handling-http-403-forbidden-when-querying-the-search-rest-service-using-the-postquery-method.aspx

Espero que os haya resultado interesante.

Un saludo

Sharepoint-search: un componente Polymer para usar la búsqueda de SharePoint On-Line

Muy buenas a todos.

En una entrada anterior, os contaba cómo usar la búsqueda de SharePoint a través de su servicio API REST. El enlace ha dicha entrada es la siguiente:

Search Driven Development. Usando la API REST de SharePoint para el servicio de búsqueda

Al final de esta entrada os contaba que como objetivo, tenía el desarrollo de un componente Polymer que permitiera usar este servicio, algo que me resultaba muy útil e interesante. Después de varios días de trabajo, quería compartir con vosotros una primera «versión» de dicho componente. Aún queda trabajo por delante, pero ya hay un punto de partida funcional a partir del cual seguir trabajando.

El componente debía cumplir con los siguientes requisitos:

  • Permitir la posibilidad de usar refinadores.
  • Permitir la posibilidad de establecer opciones de ordenamiento.
  • Controlar la paginación y el número de resultados por página en los resultados de búsqueda.
  • Dar la posibilidad de estilar el contenido del componente en función de como se desee.

El componente «sharepoint-search» nos permite cubrir estos requisitos. Para usarlo, necesitaremos tener cargados los ficheros de Polymer y los propios del componente y utilizaremos la etiqueta de la siguiente manera:

<sharepoint-search tenant="organizer" clientid="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" refinement="author,size,created" ordination="size,created" pageElement="15">
</sharepoint-search>

Para que funcione correctamente, necesitaremos el clientID de una aplicación de Azure AD con los permisos para el servicio de búsqueda de SharePoint concedidos, y obviamente estar logado en nuestra aplicación contra esa aplicación de Azure AD, para ello podemos usar el componente para la autenticación que creé en una entrada anterior y seguir los correspondientes pasos para obtener el clientid:

Registrando una aplicación en Azure Active Directory

login-azuread: Un Componente Web con Polymer y Javascript ADAL

Además deberemos de configurar la aplicación de nuestra Azure AD para permitir el OAuth 2.0 implicit grant flow, lo haremos de la siguiente forma:

  • Acceder a la configuración de la aplicación de Azure AD correspondiente
  • Descargamos el manifiesto
  • Modificamos el parámetro oauth2AllowImplicitFlow dándole el valor true en lugar de false que tiene establecido inicialmente
  • Cargamos de nuevo el manifiesto modificado

El componente «sharepoint-search» permite configurar los siguientes parámetros:

  • tenant: Indicar el tenant al que nos queremos conectar
  • clientid: Clientid de la aplicación con los permisos adecuados
  • refinement: Propiedades para las cuales queremos obtener refinamiento
  • ordination: Propiedades por las que queremos hacer ordenación
  • pageElement: Número de elementos por página a mostrar

Una vez que lo añadimos, funciona como vemos a continuación.

sharepointsearchinit

sharepointsearchsearched

Por defecto, el componente nos muestra los resultados en una lista con el título del documento y la posibilidad de descargar el mismo. Aunque como se indica a continuación, en futuras mejoras, se trabajará en flexibilizar este aspecto del componente.

Os dejo el enlace a mi cuenta de GitHub donde podéis descargar el código del componente web y donde podréis encontrar información sobre cómo usarlo.

https://github.com/jcroav/sharepoint-search

Y esto es todo por hoy, este componente aún tiene áreas de mejora, a las que intentaré dedicarle tiempo en las próximas semanas. Os dejo algunas de las ideas:

  • El componente está pensado para que los resultados de búsqueda usen a su vez un subcomponente para ser mostrados, queda pendiente de desarrollar esa parte.
  • Completar el componente con la funcionalidad de sugerencias
  • Mejorar toda la parte de estilado
  • Refactorizar y trabajar sobre la optimiazación del código para hacer un componente más robusto

Como siempre, todo aquel que quiera colaborar, o quiera usar el componente que os he mostrado hoy para mejorarlo, es libre de hacerlo. El objetivo de estos desarrollos, a parte de para aprender a usar las funcionalidades de Office 365 y SharePoint On-Line, es contribuir a la comunidad.

Un saludo y espero que os sea útil

login-azuread: Un Componente Web con Polymer y Javascript ADAL

Hola a todos,

En la entrada anterior os contaba como se podía usar la librería Javascript ADAL para autenticar una aplicación contra Azure AD usando Javascript y terminaba diciendo que os contaría sobre el componente Polymer que había desarrollado para la autenticación.

Autenticación de nuestra Aplicación usando Javascript ADAL

Lo que he hecho ha sido crear un componente web que permite de una forma sencilla hacer la autenticación y que añadiéndolo a cualquiera de nuestras aplicaciones, podamos gestionar el sign in contra Azure AD de la misma. El código del componente web lo podéis encontrar en mi cuenta de GitHub.

Azuread-login en GitHub

El modo de empleo es muy sencillo, solo tendremos que insertar la siguiente línea en donde queramos tener nuestro modulo de login.

<login-azuread domain="<tenantdomainhere>" clientid="<clientidhere>"
</login-azuread>

Antes, deberemos haber registrado la aplicación en Azure AD, lo que nos proporcionará un client ID. Para que el componente funcione correctamente, deberemos indicar en el atributo domain, el dominio de nuestro tenant y en el atributo clientid el dato obtenido tras el registro de la aplicación en Azure AD.

¿Cuál es el resultado?

Vamos a ver como funciona el componente web una vez lo usamos en un ejemplo.

Cuando un usuario no se ha autenticado.

azureadlogin1

azureadlogin2

Cuando un usuario se ha autenticado
azureadlogin3

Además el componente se ha diseñado de manera que los elementos que lo forman, no se encuentran dentro del Shadow DOM que usa Polymer, sino que están renderizados por el DOM de la página, de manera que podemos estilarlos por medio del CSS de nuestra aplicación. Para ello, solo tenemos que redefinir las clases loginad-name, loginad-a, logoutad-a.

¿Vemos algo de código?

El código es muy sencillo, y aunque lo podéis ver en GitHub os lo pongo aquí y explico algunos detalles. El componente lo forman dos ficheros, uno .html y otro .js

<script src="../../Scripts/polymer.min.js"></script>
<script src="../../Scripts/adal.js"></script>

<polymer-element name="login-azuread" attributes="domain clientID" />
    <template>
        <content select="span"></content>
        <content select="a"></content>
        <content select="a"></content>
    </template>
    <script src="login.js"></script>
</polymer-element>

El fichero .html contiene la definición del componente web. Como véis el template utiliza la etiqueta content, que nos va a permitir que el contenido del componente no esté renderizado en el Shadow DOM y si en el DOM de la página.

window.config = {
    tenant: '',
    clientId: '',
    postLogoutRedirectUri: window.location.origin,
    cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
};

var authContext;

Polymer({
    userName: '',
    domain: '',
    clientID: '',
    ready: function () {
        window.config.tenant = this.domain;
        window.config.clientId = this.clientID;

        authContext = new AuthenticationContext(config);

        var isCallback = authContext.isCallback(window.location.hash);
        authContext.handleWindowCallback();

        if (isCallback &amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp; !authContext.getLoginError()) {
            window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
        }

        // Check Login Status, Update UI
        var user = authContext.getCachedUser();

        if (user)
            this.userName = user.userName;
        else
            this.userName = '';

    },
    attached: function () {
        var name = document.createElement("span");
        name.innerHTML = this.userName;
        name.className = "loginad-name";

        var login = document.createElement("a");
        login.href = "javascript:;";
        login.innerText = "Login";
        login.className = "loginad-a";
        login.addEventListener("click", this.login, true);

        if (this.userName != '')
            login.style.display = "none";
        else
            login.style.display = "inline-block";

        var logout = document.createElement("a");
        logout.href = "javascript:;";
        logout.innerText = "Logout";
        logout.className = "logoutad-a";
        logout.addEventListener("click", this.logout, true);

        if (this.userName != '')
            logout.style.display = "inline-block";
        else
            logout.style.display = "none";

        this.appendChild(name);
        this.appendChild(login);
        this.appendChild(logout);
    },
    login: function () {
        authContext.login();
    },
    logout: function () {
        authContext.logOut();
    }
});

La particularidad de este código es que, como podemos ver, utiliza la función ready de Polymer para cargar el objeto de configuración y comprobar si está autenticado el usuario y la función attached para inyectar todos los elementos que forman el componente, con la funcionalidad de cada uno.

¿Es mejorable?, como siempre, claro que lo es. Y me encantaría recibir feedback de qué más podríamos hacer para que este componente fuera realmente útil y pudiéramos usarlo en nuestras aplicaciones como una alternativa más.

Saludos, espero que lo hayáis encontrado interesante.

Autenticación de nuestra Aplicación usando Javascript ADAL

Muy buenas a todos,

En una entrada anterior, vimos como podíamos desarrollar una aplicación web con MVC .NET que nos permitiera acceder a la información guardada en nuestro Office 365. Una parte importante de ese desarrollo era la autenticación contra Azure AD, lo que se hacía por medio del paquete Nugget ADAL (Active Directory Authentication Library).

Hace unos días tuve conocimiento de una librería de Javascript que nos permitía hacer la autenticación contra Azure AD (conocida como Javascript ADAL) y que se podía utilizar en el desarrollo de Single Page Applications de una forma muy sencilla. Así que decidí probarlo para seguir abriendo y conociendo el abanico de oportunidades para trabajar contra Azure AD y luego con la API de Office 365. El primer paso obviamente será el login para después acceder y consultar la API usando CORS.

Así que en la entrada de hoy, os voy a mostrar cómo hacer el login usando esta librería de Javascript. Para el ejemplo he creado un proyecto de MVC vacío en el que he hecho todo el desarrollo. Antes de nada, os dejo, como siempre, los enlaces que me han servido de guía y donde creo que hay información muy valiosa sobre esta librería.

http://www.cloudidentity.com/blog/2015/02/19/introducing-adal-js-v1/

https://github.com/AzureADSamples/SinglePageApp-jQuery-DotNet

Disponemos de una versión de la librería integrada con Angularjs y otra versión que podremos usar con JQuery y otros frameworks que será la versión que voy a usar en mi ejemplo.

Empezando con el ejemplo

Obviamente el primer paso en cualquier caso, es registrar nuestra aplicación en el Azure Active Directory de nuestro Tenant. Tras esto, tenemos que descargar el fichero adal.js.

El siguiente paso que he seguido es crear la vista del ejemplo, que es tal y como veréis a continuación

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <script src="~/Scripts/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
    <script src="~/Scripts/adal.js"></script>
    <script src="~/Scripts/App/app.js"></script>
</head>
<body>
    <div>
        <h2>Index</h2>

        <a href="javascript:;" class="app-login">Login</a>
        <a href="javascript:;" class="app-logout">Logout</a>
    </div>

</body>
</html>

Esta vista tiene dos botones, uno para hacer login y otro para hacer logout, nada más. El fichero que tiene todo el código es el fichero app.js que podéis ver ahora.

(function () {

    //1.- Initial configuration
    window.config = {
        tenant: '<tentantdomain>',
        clientId: '<clientid>',
        postLogoutRedirectUri: window.location.origin,
        cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
    };
    var authContext = new AuthenticationContext(config);

    //2.- Handle redirect after login
    var isCallback = authContext.isCallback(window.location.hash);
    authContext.handleWindowCallback();

    if (isCallback && !authContext.getLoginError()) {
        window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
    }

    //3.- Check for authenticated information
    var user = authContext.getCachedUser();
    if (user) {
        console.log(user);
    } else {
        console.log("error");
    }

    //4.- Handle events for login & logout
    var $signInButton = $(".app-login");

    $signInButton.click(function () {
        authContext.login();
    });

    var $signOutButton = $(".app-logout");

    $signOutButton.click(function () {
        authContext.logOut();
    });
}());

El código de este ejemplo es una simplificación del ejemplo de Github que os he puesto un poco más arriba y que he usado para entender el proceso que seguía. Lo único que hace este código es mostrar en la consola de depuración los datos del usuario si está logueado o mostrar un texto de error si no lo está. Los puntos más importantes del código son los siguiente:

  1. El primer paso es crear el objeto de configuración, con los datos del tenant y client id que hemos obtenido al registrar la aplicación
  2. A continuación se indica el callback que maneja la redirección después del login
  3. Se comprueba y se muestra la información de logueo del usuario
  4. Por último se crean los eventos que dispararán el login y el logout asociados al click en los enlaces

Y esto es todo, si lo probamos comprobaremos que el resultado es el esperado, cuando no estamos logueados, nos muestra la pantalla de signin de windows para indicar nuestro usuario y contraseña del Azure AD de nuestro tenant.

Lo que os voy a enseñar en próximas entradas es un componente web de Polymer que he creado que utiliza este código para implementar la funcionalidad de signin y signout de una manera muy sencilla y que creo que puede ser de utilidad. Este componente, de hecho ya está en mi GitHub

Un saludo a todos y buen finde.

Usando Bower y Grunt en Visual Studio 2013

Muy buenas a todos,

Hoy os quiero contar los pasos que he dado para usar en mis proyectos de Visual Studio 2013 bower, usándolo como gestor de paquetes en el lado del cliente, y grunt, como task runner. Ambos están siendo muy comúnmente usados en el mundo del desarrollo de aplicaciones web y si bien, la versión de Visual Studio 2015 los incluye de forma nativa, podemos configurar nuestros proyectos web de Visual Studio 2013 para usar estas dos herramientas tan útiles.

Para todo aquel que le pueda interesar, dejo la entrada del blog en la que me he basado para hacer toda la configuración, aunque si accedéis veréis que en el post, se usa otro gestor de tareas llamado gulp en lugar de grunt.

http://blogs.msdn.com/b/cdndevs/archive/2015/02/17/using-bower-with-visual-studio-2013.aspx

Pero antes de entrar en detalle, vamos a ver qué son exactamente cada una de estas herramientas.

¿Qué es Bower?

Bower works by fetching and installing packages from all over, taking care of hunting, finding, downloading, and saving the stuff you’re looking for.

La primera pregunta que se nos plantea viendo lo que hace Bower es ¿Y Nugget?. La respuesta a esta pregunta es muy sencilla. Desde su aparición Nugget se ha descubierto como una herramienta muy eficaz para la gestión de paquetes de servidor, pero no así para paquetes de cliente. Bower se ha convertido en el estándar de facto en el desarrollo web. Es poco probable, que un desarrollador que crea una nueva librería en Javascript la ponga disponible como paquete Nugget, sin embargo es frecuente que esté disponible en Bower en todas sus versiones. Esto provoca desactualización o imposibilidad de usar determinadas librerías a través de Nugget, lo que desaconseja su uso como herramientas de gestión de paquetes del lado del cliente.

¿Qué es Grunt?

In one word: automation. The less work you have to do when performing repetitive tasks like minification, compilation, unit testing, linting, etc, the easier your job becomes.

En resumen, con Grunt vamos a poder automatizar determinadas tareas que van a ayudar a tener nuestra aplicación lista para poner en producción con acciones como: minimizar y agrupar los estilos, minimizar y agrupar los javascript, etc. En la entrada veremos una tarea que he creado que puede ser necesaria y recomendable cuando usamos Bower.

Configurando y usando Bower

Para la configuración de Bower, podéis fácilmente seguir la entrada del blog de la msdn sin ningún problema, es muy sencilla de seguir y está muy detallada, de hecho yo la he seguido y es lo que de forma simplificada explicaré por aquí, solo hay una cosa que cambia, porque a mi al menos, o no me quedaba claro en la entrada inicial o no estaba debidamente reflejado. Vamos a ello.

    1. El primer paso es instalar node.js, ya que tanto Bower como Grunt lo utilizan. Enlace de descarga de node.js
    2. A continuación, abrimos la línea de comandos e instalamos bower
npm install bower -g
    1. Tras esto, necesitaremos tener instalado Git, Bower utiliza Git para descargar los paquetes. Concretamente instalaremos msysgit. Durante la instalación, tendremos que seleccionar la opción que vemos en la imagen a continuación para que se realice toda la configuración correctamente

gitinstall

    1. Ya tenemos listo Bower para ser utilizado. Si lo vamos a usar dentro de un proyecto de MVC, antes tendremos que tener en cuenta que por defecto, la plantilla de este tipo de proyectos en Visual Studio incluye una serie de paquetes, entre ellos: bootstrap, jquery, modernizr, etc. Por lo que antes de comenzar a usar bower en nuestro proyecto, tenemos que desinstalarlos. Para ello accedemos a la consola de Nuget y ejecutamos el siguiente comando para cada uno de los paquetes
Uninstall-Package <package>

Ahora que ya tenemos listo Bower vamos a usarlo dentro de nuestro proyecto. Esto lo haremos a través de la línea de comandos. En mi caso, no he usado la interfaz de línea de comandos propia de Windows (cmd), ya que me daba errores al usarla con Bower y Git, y buscando por internet vi que recomendaban usar una interfaz de línea de comandos que se instala con msysgit. Esta es Git Bash.

Vamos ahora a entrar al directorio del proyecto, y comenzaremos con el uso de Bower. Lo primero que haremos será iniciar el fichero de configuración de Bower. Para ello ejecutamos:

bower init

Una vez creado el fichero bower.json, podemos comenzar a instalar paquetes, esto lo haremos de la siguiente forma.

bower install jquery --save
bower install polymer --save

Una Buena Práctica recomendada para el uso de los paquetes que se obtienen con Bower, es no agregar directamente todos los archivos descargados con Bower a nuestro proyecto, ya que los paquetes en muchas ocasiones contienen más archivos de los que realmente necesitaremos, por lo que se recomienda copiar al proyecto solo aquellos archivos que realmente sean necesarios. ¿Cómo hacemos esto, manualmente?. La respuesta evidentemente es no, para esto tenemos nuestro Task Runner, Grunt 😉

Configurando y usando Grunt

En primer lugar, para añadir el soporte a Grunt para nuestro proyecto vamos a crear dos ficheros que añadiremos a la raíz del mismo package.json y gruntfile.js. El fichero package.json inicialmente contendrá la siguiente información.

{
"name": "WebApplication1",
"version": "1.0.0",
"description": "WebApplication package project",
"main": "gruntfile.js",
"license": "ISC";
}

A continuación y como ejemplo, la tarea que vamos a crear va a copiar los ficheros necesarios de los paquetes que hemos obtenido mediante Bower a la carpeta Scripts, donde se encuentran los ficheros js de nuestro proyecto. El código del fichero gruntfile.js es el siguiente:

module.exports = function (grunt) {

    grunt.initConfig({
        copy: {
            main: {
                files: [
                  {
                      expand: true,
                      cwd: '../bower_components/webcomponentsjs/',
                      src: ['webcomponents.js'],
                      dest: 'Scripts'
                  },
                  {
                      expand: true,
                      cwd: '../bower_components/jquery/dist',
                      src: ['jquery.min.js'],
                      dest: 'Scripts'
                  }
                ]
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.registerTask('build', ['copy']);
};

Obviamente, aún podríamos hacer algunas tareas de minimización y agrupamiento de ficheros con grunt y que serían necesarias, aunque eso lo dejo para más adelante, es posible que en alguna entrada futura os cuente como hacerlo.

Para que funcione correctamente, necesitamos un plugin de Grunt, «grunt-contrib-copy». Cuando necesitemos añadir determinados plugins para nuestras tareas de Grunt, lo haremos desde la línea de comandos usando la utilidad npm de node.js. Nos vamos al directorio del proyecto y ejecutamos el siguiente comando.

npm install grunt-contrib-copy --save-dev

Una vez que ya tenemos instalado el plugin, desde la línea de comandos en el directorio en el que está nuestro archivo gruntfile.js (la raíz del proyecto), ejecutamos grunt. Esto llevará a cabo la tarea que hemos creado. La primera vez que ejecutamos grunt, tendremos que añadir manualmente los archivos que se han añadido a la tarea al proyecto, aunque esta acción no tendremos que volver a hacerla.

Como hemos visto, hasta ahora estamos usando la línea de comando para ejecutar Grunt. Pero, ¿Y si queremos que se ejecute automáticamente Grunt cuando hacemos el deploy del proyecto, esto es posible?, la respuesta es si. Para esto tenemos varias extensiones de Visual Studio que nos lo permiten. En mi caso he utilizado Task Runner Explorer.

La descargamos y la instalamos y ya podremos usarla en nuestro Visual Studio 2013. Vamos a View->Other Windows->Task Runner Explorer y cuando se abre la nueva ventana, lo primero que hacemos es pulsar sobre el botón actualizar para que capture todas las tareas creadas si aún no lo habia hecho.

taskrunnerexplorer

Aquí vemos que podemos configurar si queremos que una determinada tarea se ejecute antes del deploy de la solución, con lo que podremos automatizar correctamente la ejecucion de Grunt y no tendremos que hacerlo manualmente a través de la línea de comandos.

run

binding

Os dejo para terminar, algunos enlaces más de utilidad relacionado con lo que hemos visto en la entrada:

http://gruntjs.com/
http://bower.io/
https://visualstudiogallery.msdn.microsoft.com/8e1b4368-4afb-467a-bc13-9650572db708

Y esto es todo por hoy, yo a partir de ahora voy a utilizar estas dos herramientas en todos mis proyectos, por las ventajas que ofrecen, espero que todos podáis hacerlo.

Un saludo y hasta la próxima

Usando Promises con JQuery y API REST de SharePoint

Hola de nuevo a todos,

Empezando a trabajar con Javascript de forma intensiva, me he topado con la necesidad de usar un concepto del que había oído hablar hace tiempo, pero que hasta ahora no me había hecho falta. Este concepto es lo que se conoce como Promesas o Promises.

Cuando estamos trabajando con varias APIs a través de peticiones asíncronas o con una API pero realizamos varias peticiones a la vez, al final te puedes encontrar con el problema de que, peticiones que necesitas que se ejecuten después que otras lo están haciendo antes, por la propia naturaleza de las llamadas asíncronas. Puede ocurrir también que, funciones que necesitas que se ejecuten al finalizar las llamadas asíncronas lo estén haciendo antes. Para evitar esto puedes pensar en un primer momento en una montón de llamadas asíncronas anidadas en el callback de éxito de las llamadas asíncronas o por el contrario, usar las promesas.

¿Que son las promesas?

Para explicar lo que son las promesas, voy a tomar prestado la explicación de la siguiente entrada de un blog que he encontrado:

http://www.funcion13.com/2012/03/26/comprendiendo-promesas-y-deferreds-en-jquery/

La promesa (promise) en sí, es un objeto que representa un evento en el tiempo, como la llegada de datos de una petición asíncrona o el fin del procesamiento de todos los datos obtenidos en una función asíncrona. Un objeto promise es una versión de solo lectura de un objeto deferred.

Los deferred comienzan en estado pendiente, y es lógico si lo pensáis, pudiendo pasar a estado resuelto (la tarea se completó) o rechazado (si es que falló). En el momento en que un deferred cambie su estado a resuelto o rechazado no podrá cambiar nunca más.

Vamos a usar las promesas para manejar peticiones asíncronas, y controlar que se reciba una respuesta sea correcta o no. Además, con JQuery, podremos controlar, la respuesta de varias peticiones asíncronas por medio de promesas que se ejecutan simultáneamente. El código javascript, además, quedará mucho más ordenado.

Os dejo algunos enlaces que he usado yo para entender bien el concepto de promises.

http://www.danieldemmel.me/blog/2013/03/22/an-introduction-to-jquery-deferred-slash-promise/

http://joseoncode.com/2011/09/26/a-walkthrough-jquery-deferred-and-promise/

http://api.jquery.com/deferred.promise/

¿Cómo se usan las promesas?

En el ejemplo que os voy a mostrar a continuación, he usado las promesas para trabajar con la API REST de SharePoint. En mi caso, en una aplicación que estoy desarrollando, necesitaba hacer varias peticiones API REST para saber si varias listas existían para, en función de si lo hacían o no indicarlo al usuario para darle la oportunidad de crearlas.

El problema me surgía por la dificultad de controlar el resultado de las peticiones, ya que al ser asíncronas, las evaluaciones de su resultado se hacían antes de que la petición se hubiera realizado realmente, por lo que no funcionaba como quería que lo hiciera.

Para ello he usado el concepto de promesas, por medio de ellas, puedo controlar que todas las peticiones se hagan y den un resultado, y esperar hasta que se obtenga la respuesta de todas para llevar la acción que corresponda. Voy a mostraros el código y posteriormente explicarlo, en mi caso he usado la librería JQuery y el objeto Deferred de la misma:

function ExistList(listname) {

        var deferred = $.Deferred();

        var appweburl = GetQueryStringParameter("SPAppWebUrl");
        var hostweburl = GetQueryStringParameter("SPHostUrl");

        var executor = new SP.RequestExecutor(appweburl);

        executor.executeAsync({
            method: "GET",
            url: appweburl + "/_api/SP.AppContextSite(@target)/web/Lists/GetByTitle('" + listname + "')?@target='" + hostweburl + "'",
            headers: { "Accept": "application/json; odata=verbose" },
            success: function (data) {
                deferred.resolve();
            },
            error: function (data) {
                console.log(data);
                deferred.reject();
            }
        });

        return deferred.promise();
    }

function Validation(){

            var list1 = "List1";
            var list2 = "List2";

            $.when(ExistList(list1), ExistList(list2))
             .done(function () {

             })
             .fail(function () {
                 RedirectApp("Install.aspx");
             });
                
        }
  • El primer aspecto importante se encuentra en la función ExistList y es el objeto deferred, se crea al inicio de la función y al final de la función se devuelve la promesa. Dentro de la función se hace la petición asíncrona y en el callback de éxito y de error de la misma llamada, se indica si la promesa se ha cumplido (por medio de deferred.resolve()) o no (por medio de deferred.reject()) respectivamente.
  • En la función de validación el primer elemento importante es la llamada $.when() a la que le indicaremos como parámetro (tal y como podemos ver), todas las promesas que queremos que espere a obtener resultado. Los callback no se ejecutarán hasta que no se haya recibido la respuesta de todas las promesas indicadas como parámetro de la llamada.
  • En el caso de que todas las promesas hayan respondido positivamente se utiliza el callback de la llamada done.
  • En el caso de que alguna de las promesas haya respondido negativamente se ejecuta el callbak de la llamada fail

En ocasiones también podremos encontrarnos que se usa la llamada then que lleva como parámetros los dos callback de éxito y error, pero a mi me ha resultado más claro usar la llamada done y fail.

Cuando trabajamos con JQuery y usamos la llamada $.ajax(), ya de por sí devuelve una promesa por lo que no debemos encapsularla en una función para crear el objeto deferred.

Y con esto espero que si alguien no sabía o no había trabajado habitualmente con promesas, encuentre aquí como trabajar con ellas. Aunque el trabajo con promesas no es una novedad, hasta ahora no había necesitado trabajar con ellas, me ha resultado muy fácil, además de que el código queda muchísimo más limpio y organizado.

Un saludo a todos.

Añadiendo elementos a una lista usando la API REST de SharePoint 2013

Muy buenas a todos.

Hoy quiero dar un paso más en el uso de la API REST de SharePoint 2013 y el conocimiento de la misma. Hasta ahora, solo la había usado para obtener datos de SharePoint: de usuarios, listas y sitios. Lo que hoy quiero mostrar en este post es, cómo añadir a través de API REST elementos a una lista. Os dejo en primer lugar algunos enlaces a post donde trataba sobre todo lo anterior que os comentaba

Acceso a datos desde una Aplicación en SharePoint 2013 usando API Rest

Introducción a la API REST de SharePoint 2013 (Usuarios y grupos)

Introducción a la API REST de SharePoint 2013

Para poder cumplir el objetivo que me he propuesto, voy a crear una SharePoint Hosted App, en la que implementaré todo el funcionamiento. Va a ser una App sencilla, por lo que voy a utilizar la estructura que por defecto se crea con el proyecto, y modificaré los ficheros Default.aspx y App.js.

Además en esta aplicación vamos a añadir una Lista Personalizada que llamaremos Noticias, y que tendrá además del campo Title, uno llamado Subject y otro Body y que añadiremos a través de Visual Studio.

Código del fichero Default.aspx

<%-- Las 4 líneas siguientes son directivas ASP.NET necesarias cuando se usan componentes de SharePoint --%>

<%@ Page Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" MasterPageFile="~masterurl/default.master" Language="C#" %>

<%@ Register TagPrefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%-- El marcado y el script del elemento Content siguiente se pondrán en el elemento <head> de la página --%>
<asp:Content ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
    <script type="text/javascript" src="../Scripts/jquery-1.9.1.min.js"></script>
    <script type="text/javascript" src="/_layouts/15/sp.runtime.js"></script>
    <script type="text/javascript" src="/_layouts/15/sp.js"></script>
    <script type="text/javascript" src="/_layouts/15/SP.RequestExecutor.js"></script>
    <meta name="WebPartPageExpansion" content="full" />

    <!-- Agregue sus estilos CSS al siguiente archivo -->
    <link rel="Stylesheet" type="text/css" href="../Content/App.css" />

    <!-- Agregue el código JavaScript al siguiente archivo -->
    <script type="text/javascript" src="../Scripts/App.js"></script>
</asp:Content>

<%-- El marcado del elemento Content siguiente se pondrá en el elemento TitleArea de la página --%>
<asp:Content ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server">
    Page Title
</asp:Content>

<%-- El marcado y el script del elemento Content siguiente se pondrán en el elemento <body> de la página --%>
<asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server">

    <div>
        <p id="message">
            <!-- El contenido siguiente se reemplazará con el nombre de usuario cuando ejecute la aplicación; vea App.js -->
            initializing...
        </p>
    </div>

    <label for="title">Título:&nbsp;</label>
<input id="title" type="text" />

    <label for="title">Subtítulo de la noticia:&nbsp;</label>
<input id="subtitle" type="text" />

    <label for="title">Cuerpo:&nbsp;</label>
<textarea id="body" rows="3" cols="30"></textarea>
    


    <button id="submit" >Añadir Nueva Noticia</button>

</asp:Content>

El código de este fichero es muy sencillo, tan solo se ha modificado el mismo para añadir, los inputs que nos van a permitir insertar una nueva noticia y el botón correspondiente y una etiqueta p donde vamos a insertar las listas de forma consecutiva.

Un aspecto que si que es importante está en la línea 18, donde se encuentra la referencia al script RequestExecutor.js con la librería Cross-Domain que vamos a usar para hacer las peticiones con JSOM y API REST.

Código del Fichero App.js

'use strict';

var hostweburl;
var appweburl;

$(document).ready(function () {
    //Get the URI decoded URLs.
    hostweburl =
        decodeURIComponent(
            getQueryStringParameter("SPHostUrl")
    );
    appweburl =
        decodeURIComponent(
            getQueryStringParameter("SPAppWebUrl")
    );

    // resources are in URLs in the form:
    // web_url/_layouts/15/resource
    //var scriptbase = hostweburl + "/_layouts/15/";

    LoadNews(appweburl);

    $("#submit").click(function () {
        var title = $("#title").val();
        var subtitle = $("#subtitle").val();
        var body = $("#body").val();

        var executor = new SP.RequestExecutor(appweburl);

        executor.executeAsync({
            method: "POST",
            url: appweburl + "/_api/web/Lists/GetByTitle('Noticias')/items",
            body: "{'__metadata': {'type': 'SP.Data.NoticiasListItem'},'Title': '" + title + "', 'Subject1': '" + subtitle + "', 'Body1': '" + body + "'}",
            headers: {
                "accept": "application/json;odata=verbose",
                "content-type": "application/json;odata=verbose"
            },
            success: function (data) {
                console.log(data);
                LoadNews(appweburl);
            },
            error: function (data) {
                console.log(data);

            }
        });

        
        return false;
    })
});

function LoadNews(appweburl)
{
    var executor = new SP.RequestExecutor(appweburl);

    executor.executeAsync({
        method: "GET",
        url: appweburl + "/_api/web/Lists/GetByTitle('Noticias')/items",
        headers: { "Accept": "application/json; odata=verbose" },
        success: onLoadSuccess,
        error: onLoadError
    });
}

function onLoadSuccess(data) {
    var jsonObject = JSON.parse(data.body);
    var announcementsHTML = "";

    var results = jsonObject.d.results;
    for (var i = 0; i < results.length; i++) {
        announcementsHTML = announcementsHTML +
            "<p><h1>" + results[i].Title +
            "</h1>" + results[i].Body1 +
            "</p><hr>";
    }

    document.getElementById("message").innerHTML =
        announcementsHTML;
}

function onLoadError() {
    alert("Error loading Data");
}

function getQueryStringParameter(paramToRetrieve) {
    var params =
        document.URL.split("?")[1].split("&");
    var strParams = "";
    for (var i = 0; i < params.length; i = i + 1) {
        var singleParam = params[i].split("=");
        if (singleParam[0] == paramToRetrieve)
            return singleParam[1];
    }
}

En el archivo app.js, para facilitar el desarrollo del código, se usa JQuery. Además, vamos a destacar los siguientes aspectos del mismo:

  • La función LoadNews(), hace una petición REST para obtener las noticias de la lista que se creó anteriormente, y se usa la función onLoadSuccess para leer e imprimir los datos en pantalla
  • En la línea 23, se crea la función que asocia al evento click del botón la solicitud REST para insertar el elemento. Se utiliza el objeto SP.RequestExecutor para hacer la petición
  • La línea 33 también es importante, por medio del parámetro body se le indica a través de JSON, el elemento que se desea añadir. El primer parámetro indica el tipo de dato. Éste, será siempre, dependiendo de la lista, SP.Data.NOMBREDELALISTAListItem.
  • El parámetro url indicará, la dirección de API REST para acceder a los elementos de la lista en donde se quiere añadir el parámetro
  • Como lo que queremos es insertar un elemento, usaremos en esta ocasión como valor del parámetro method de la función POST

Viendo el resultado de la App

Vamos a ver el resultado de probar la App que acabamos de desarrollar.

Captura de pantalla 2015-02-06 a las 0.16.04

A continuación veremos como al pulsar sobre el botón de añadir aparece una nueva noticia en la lista de noticias.

Captura de pantalla 2015-02-06 a las 0.16.24

En el código, cuando la operación se realizaba correctamente, se escribía en consola el resultado de la respuesta. En la siguiente captura lo podéis ver, donde se confirma que la petición devuelve un resultado correcto.

Captura de pantalla 2015-02-06 a las 0.24.00

Y esto es todo por hoy. Espero que os haya resultado interesante como con la API REST se puede, tanto obtener como escribir información en SharePoint. En próximos artículos, quiero centrarme en ejemplos de SharePoint Provider Hosted App y en cómo trabajar con la otra API, CSOM, que me queda por conocer.

Saludos.

Polymer. Usando Arrays en el modelo de datos del Componente Web

Muy buenas a tod@s.

Sigo profundizando en este framework de Javascript, con el objetivo de llegar a un conocimiento del mismo que me permita poder usarlo activamente en mis desarrollos en proyectos reales. En post anteriores de esta serie relacionada con Polymer, habíamos visto por un lado la estructura básica para definir un elemento, y por otro cómo usar CSS dentro del componente definido y cómo renderizar elementos dentro del mismo.

Introducción a Polymer. Mi primer componente web

Polymer: CSS y cómo renderizar elementos dentro del componente web

Hoy quiero mostrar cómo podríamos trabajar con un array dentro de nuestro modelo de datos del componente. Es muy sencillo, así que voy a pasar directamente a mostraros el código del componente y mostraros los aspectos importantes del mismo.

<link rel="import" href="../bower_components/polymer/polymer.html">

<polymer-element name="example-posts">
	<template>
		<ul>
			<template repeat="{{post in posts}}">
				<li>
					<h2>{{post.title}}</h2>
					<p>{{post.body}}</p>
				</li>
			</template>
		</ul>
	</template>
	<script>
		Polymer({
			posts: [
				{title: 'Example post 1', body: 'This is the body of this post'},
				{title: 'Example post 2', body: 'This is the body of this post. The second of this array'},
				{title: 'Example post 3', body: 'This is the body of the last post'}
			]
		});
	</script>
</polymer-element>

El código, como os decía, por sí solo es muy sencillo, pero vamos a explicar los aspectos más importantes del mismo.

  • El atributo repeat del template, le dice a Polymer que tiene que crear un fragmento de código para cada elemento del array.
  • En el interior de ese template se establece cómo será el aspecto de ese fragmento.
  • Por medio de la expresión {{post.title}} o {{post.body}} se pueden acceder a las propiedades de los objetos del array.

El resultado de usar este componente en un proyecto web.

Captura de pantalla 2015-01-31 a las 17.25.03

Por supuesto podríamos mejorar este código y usar el concepto propio de Polymer de crear Componentes Web, para crear un componente web que representara a un post, y usarlo dentro del ejemplo anterior. Vamos a ver cómo sería este código, dónde se ve la creación del componente post y como se usa posteriormente.

<link rel="import" href="../bower_components/polymer/polymer.html">

<polymer-element name="example-post" attributes="title body">
	<template>
		<li>
			<h2>{{title}}</h2>
			<p>{{body}}</p>
		</li>
	</template>
	<script>
		Polymer({
			title: '',
			body: ''
		});
	</script>
</polymer-element>

<polymer-element name="example-posts">
	<template>
		<ul>
			<template repeat="{{post in posts}}">
				<example-post title="{{post.title}}" body="{{post.body}}"></example-post>
			</template>
		</ul>
	</template>
	<script>
		Polymer({
			posts: [
				{title: 'Example post 1', body: 'This is the body of this post'},
				{title: 'Example post 2', body: 'This is the body of this post. The second of this array'},
				{title: 'Example post 3', body: 'This is the body of the last post'}
			]
		});
	</script>
</polymer-element>

Y esto es todo por hoy, con este post, completo ya por fin bastantes de los aspectos que quería trabajar de Polymer, ahora espero empezar a usar este framework de forma intensiva en mis proyectos webs, ya que en mi opinión ofrece muchas ventajas y aspectos interesantes a tener en cuenta.

Un saludo a todos

Polymer: CSS y cómo renderizar elementos dentro del componente web

Muy buenas a todos,

En una entrada anterior, os contaba cómo podríamos usar este framework para crear un componente web que fuese fácilmente reutilizable. Os contaba también, algunos de los aspectos básicos de funcionamiento de Polymer para dotar a ese componente web de atributos y el binding que incorpora el framework y las oportunidades que nos ofrece.

Introducción a Polymer. Mi primer componente web

Hoy os quiero contar otras dos funcionalidades relevantes que nos ofrece este framework Javascript en un breve post, y que espero que junto con el anterior os aporte un conocimiento más profundo sobre el uso de esta herramienta para desarrollo web.

¿Podemos estilar nuestro componente web?

Pues la respuesta es si, podemos añadir código CSS para estilar el componente. Para ver cómo hacerlo voy a utilizar el componente que había creado en el post anterior y le voy a añadir código CSS.

<polymer-element name="example-polymer" attributes="attr1 attr2">
	<template>
		<style>
			:host
			{
				width:50%;
				margin-left:25%;
				margin-top:5%;
				background-color:#DDD;
				display: block;
				padding: 1em;
			}

			h1
			{
				color:red;
			}

			.ejemploClase
			{
				color:blue;
				cursor:pointer;
			}
		</style>
		<h1>Esto es un ejemplo de uso de Polymer</h1>
		<p>{{attr1}}</p>
		<input type="text" value="{{attr2}}" />

		<h2>Usando el binding</h2>
		<p class="ejemploClase">Ejemplo de binding {{attr2}}</p>
	</template>
	<script>
		Polymer({
			attr2Changed: function(){
				if(this.attr2.length == 11)
					console.log("Esta es una cadena de 11 caracteres");
			}
		});
	</script>
</polymer-element>

Aquí hay solo un aspecto importante que destacar y es el uso de la pseudo-clase :host en el CSS. Esta clase hace referencia a la raíz del componente web, es decir, al propio componente web. Además de esto, podemos definir los estilos y clases de CSS como habitualmente lo hacemos, con la única salvedad de que solo afectarán a los elementos en el interior del componente. Vamos a ver el resultado de aplicar estos estilos.

Captura de pantalla 2015-01-24 a las 18.30.26

¿Cómo puedo renderizar elementos insertados dentro de mi componente web?

Volviendo al ejemplo del post anterior, se nos puede ocurrir, como hacemos con cualquier etiqueta del HTML estándar, hacer algo como lo siguiente:

<example-polymer attr1="Ejemplo" attr2="Otro ejemplo">
	<h3>Ejemplo</h3>
</example-polymer>

Si probamos este código, observaremos que no funciona correctamente, es decir, el navegador no renderiza los elementos hijos del componente web, lo que se traduce en que el tag h3 no se ve en nuestro navegador. ¿Quiere decir esto que no puedo insertar elementos dentro del componente web?, pues no. Podemos hacerlo, pero para ello tenemos que crear lo que se conoce en Polymer como Insertion Points, lo que le dirá al navegador donde tiene que renderizar los hijos en nuestro componente.

<polymer-element name="example-polymer" attributes="attr1 attr2">
	<template>
		<style>
			:host
			{
				width:50%;
				margin-left:25%;
				margin-top:5%;
				background-color:#DDD;
				display: block;
				padding: 1em;
			}

			h1
			{
				color:red;
			}

			.ejemploClase
			{
				color:blue;
				cursor:pointer;
			}
		</style>
		<h1>Esto es un ejemplo de uso de Polymer</h1>
		<p>{{attr1}}</p>
		<input type="text" value="{{attr2}}" />

		<h2>Usando el binding</h2>
		<p class="ejemploClase">Ejemplo de binding {{attr2}}</p>
                <content select="h3"></content>
	</template>
	<script>
		Polymer({
			attr2Changed: function(){
				if(this.attr2.length == 11)
					console.log("Esta es una cadena de 11 caracteres");
			}
		});
	</script>
</polymer-element>

Para crear lo que hemos denominado Insertion Points, utilizamos el tag content y en el atributo select indicamos la etiqueta que queramos. Con esto, estamos indicándole a Polymer que en el caso de que haya un nodo hijo con la etiqueta h3, se renderice en ese lugar. Si volvemos ahora a usar el componente web como dijimos anteriormente veremos el resultado que nos da, y como ahora si renderiza la etiqueta h3

Captura de pantalla 2015-01-24 a las 18.48.01

¿Puedo estilar esos elementos renderizados por medio de <content>?

Nuevamente la respuesta es si. Para ello haremos uso de la siguiente estructura dentro del style del componente

polyfill-next-selector { content: 'h3'; }
::content h3 {
     color: green;
}

con la pseudo-clase ::content se selecciona un Insertion Point (creado por el tag content), y con ::content h3 se selecciona cualquier h3 definido a través de content como Insertion Point.

Para los navegadores que no soportan Shadow DOM se utiliza la regla polyfill-next-selector, para indicarle cómo proceder con la pseudo-clase ::content.

Si nuevamente probamos nuestro código, vemos que el estilo se ha aplicado correctamente.

Captura de pantalla 2015-01-24 a las 19.07.18

Y esto es todo por hoy, espero que os sirva de utilidad para conocer en más profundidad formas de trabajar con Polymer. Próximamente, veremos cómo usar modelos dentro de un componente Polymer.

Un saludo a todos, buen fin de semana

Introducción a Polymer. Mi primer componente web

Hola de nuevo a todos,

Hoy os traigo un tutorial sobre una de las nuevas librerías de Javascript que comienza a utilizarse cada vez más en los proyectos web: Polymer.

Con Polymer, vamos a poder extender nuestro HTML por medio de la creación de Componentes Web de una forma sencilla y rápida. Estos Componentes Web podrán estar formados por código HTML, CSS y Javascript, lo que nos permitirá encapsular determinadas funcionalidades de nuestros proyectos, que podrán ir desde un botón hasta una aplicación completa.

La posibilidad de encapsular estos Componentes Web, nos va a permitir, como es evidente, reutilizar nuestro código en distintos proyectos, lo que nos da una interesante oportunidad y hace esta librería, una herramienta a tener en cuenta para nuestros desarrollos.

Hoy voy a mostraros un ejemplo muy básico de cómo utilizar esta librería, donde intentaré mostraros algunas de las interesantes oportunidades que nos ofrece. Vamos entonces con el ejemplo/tutorial, en el que veremos cómo crear un Componente Web y como usarlo posteriormente.

Estructura de nuestro proyecto usando Polymer

En primer lugar os muestro la estructura básica de la carpeta del proyecto web donde he creado el ejemplo.

Captura de pantalla 2015-01-12 a las 23.11.10

 

Como podéis ver la estructura básica es muy simple. Tenemos una carpeta con el núcleo de la librería «bower_components». A continuación una carpeta «elements» en donde se almacenarán los distintos Componentes Webs y por último tenemos el fichero index.html, que será la página inicial de nuestro ejemplo.

Creando el Componente Web

Para explicar cómo debemos de crear nuestro componente, voy primero a poner todo el código del mismo y después explicaré los aspecto más relevantes. El código de nuestro Componente Web lo añadiremos a un fichero dentro de la carpeta «elements», en nuestro caso el fichero «examploElement.html»

<link rel="import" href="../bower_components/polymer/polymer.html"/>

<polymer-element name="example-polymer" attributes="attr1 attr2"/>
	<template>
		<h1>Esto es un ejemplo de uso de Polymer</h1>
		<p>{{attr1}}</p>
		<input type="text" value="{{attr2}}" />

		<h2>Usando el binding</h2>
		<p>Ejemplo de binding {{attr2}}</p>
	</template>
	<script>
		Polymer({
			attr2Changed: function(){
				if(this.attr2.length == 11)
					console.log('Esta es una cadena de 11 caracteres');
			}
		});
	</script>
</polymer-element>
  • Lo primero que tenemos que hacer es cargar el núcleo de Polymer, eso lo hacemos con la línea 1 del ejemplo, cargando el archivo «polymer.html»
  • Para definir nuestro nuevo componente, utilizamos el tag polymer-element y después indicaremos el nombre de este nuevo componente por medio del atributo name. Es importante hacer referencia a que el nombre que se le da al componente web debe tener al menos un «-«, como el que he usado en este caso «example-polymer»
  • Como cualquier tag de HTML, podemos añadir atributos para nuestro componente web. Esto lo haremos dentro de la línea de definición, línea 3, usando el atributo attributes y indicando los atributos que queremos que tenga nuestro componente, separados por espacios
  • Otra parte importante en la definición del componente web, es el tag template, donde incluiremos el código HTML y CSS que formará nuestro componente. Para este primer componente solo he incluido HTML, en próximos ejemplos veremos lo que se puede hacer usando CSS también. Dentro del tag template podemos escribir todo el código HTML que deseemos.

Uno de los aspectos más interesantes que nos trae Polymer es el concepto de Data Binding. Polymer soporta data binding en dos caminos. De esta manera, se extiende el HTML y las APIs del DOM para soportar la separación entre la interfaz de una aplicación y su modelo. Las actualizaciones del modelo se reflejan en el DOM y las entradas de usuario son inmediatamente asignadas al modelo. En el ejemplo, podemos ver un sencillo modo de uso del data binding en Polymer.

  • Una propiedad que se puede usar en el data binding de Polymer se define, como podemos ver en las líneas 6, 7 y 10, de la siguiente forma {{nombredelavariable}}
  • En nuestro ejemplo, hemos creado dos propiedades que coinciden además con los atributos que definimos previamente. De manera que, lo que queremos lograr en este caso, es que en el componente se carguen, dentro del mismo, los valores que indicamos en cada uno de los atributos
  • Además, al usar la misma propiedad dentro del atributo value del input, como se ve en la línea 10 con la propiedad attr2, estamos indicándole a Polymer que se actualice dicha propiedad en todo el componente, cuando actualicemos el valor del input

Otro aspecto importante que se puede destacar es el constructor de Polymer, que se define dentro del tag script en la línea 12. El constructor de Polymer, actúa como un wrapper para document.registerElement(), aunque también dota a Polymer de algunas características como el data binding y el mapeo de eventos. El constructor de Polymer toma como elemento un objeto que define el prototipo del elemento.

En el constructor, se hace referencia al evento attr2Changed. Esto es lo que se conoce como «Changed Watcher». En Polymer, cada una de las propiedades definidas pueden ser seguidas por medio de un manejador nombrado como «nombredelapropiedadChanged», de manera que, dicho manejador se invocará automáticamente cuando se actualice la propiedad correspondiente. En el caso del ejemplo, para ver como funciona, simplemente se escribe una entrada en el log, cuando el texto de la propiedad tiene una longitud de 11 caracteres.

Usando el Componente en nuestra página web

Una vez que ya tenemos definido nuestro componente web, vamos a ver cómo tendríamos que hacer para usarlo dentro de las páginas de nuestro sitio web. En este caso, lo vamos a hacer dentro del archivo index.html. Como antes, os dejo el código para que lo veáis y después os explicaré los aspectos más importantes del mismo.

<html>
<head>
    <script src="bower_components/webcomponentsjs/webcomponents.js"></script>
    <link rel="import" href="elements/exampleElement.html" />
</head>
<body>
	<example-polymer attr1="Ejemplo" attr2="Otro ejemplo">
	</example-polymer>
</body>
</html>

En este archivo, donde vamos a usar el componente web que creamos en el paso anterior, solo hay que destacar tres aspectos importantes.

  • Por un lado, se carga el soporte de Polymer para cualquier plataforma, en la línea 3 del código.
  • El siguiente paso, es cargar el componente web que hemos definido, como vemos que se hace en la línea 4 del código
  • Y por último, ya podemos hacer referencia a nuestro componente web, donde como se puede ver, se hace uso de los atributos que habíamos indicado cuando definimos el componente.

El resultado del ejemplo que hemos desarrollado a lo largo del tutorial lo podemos ver a continuación.

Captura de pantalla 2015-01-12 a las 23.48.53

Bajo mi punto de vista, con este pequeño ejemplo, ya se pueden apreciar claramente algunas de las ventajas a la hora de hacer código reutilizable y encapsulado con Polymer. En los próximos días, veremos otro ejemplo donde se siguen usando aspectos de Polymer que no hemos visto hoy.

Os dejo el enlace a la página principal del proyecto Polymer donde podéis encontrar muchas más información sobre el mismo

https://www.polymer-project.org/

Espero que lo hayáis encontrado interesante.

Saludos.