>

Usando FlightJS (de Twitter) para una integración de Facebook preparada para producción

Francisco Javier Rodriguez Gallego     Colaboraciones    30/12/2016

Continuando con los artículos anteriores sobre la integración con Facebook (1 y 2), vamos a mostrar cómo organizar tu código para que no tengas que copiar y pegar el mismo código por diferentes páginas de tu sitio. ¿Por qué hacer esto? Te podrás estar preguntando. La respuesta es fácil: Si tienes que realizar una modificación, sólo tendrás que realizarla donde tengas definida la función mientras que en el resto de páginas sólo llamas a esa función.

No voy a entrar en profundidad en la ingeniería de software, tan sólo en los temas estrictamente necesarios para que podamos desarrollar el artículo. Vamos a comenzar con copiar y pegar código.

Copiar y pegar código es un antipatrón de diseño. Aunque puede parecer algo complicado es simplemente algo que se hace para conseguir una solución, pero esta solución es mala. En este caso, si copiamos y pegamos el código necesario para conectarse con Facebook, en vez de construir una solución genérica, tendremos en cada página que queramos usar Facebook el siguiente código JavaScript.

(function (d, s, id) {
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)) return;
    js = d.createElement(s);
    js.id = id;
    js.src = thisFacebookSdk;
    fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));

Como puedes observar, tenemos la versión del api que se utiliza en la url de conexión. Si quisiéramos modificar la versión, tendríamos que modificar todas las páginas donde se haya incluido el código. Una posible solución es poner la url en un fichero de propiedades, pero ¿qué pasa si hay que modificar el resto del código? Ahora puede parecerte fácil hacerlo ya que acabas de realizar la integración, pero como bien sabes y se ha explicado en los anteriores artículos, las versiones del api cambian y es posible que tengas que hacer modificaciones. Dentro de dos años, ¿te acordarás de lo que hiciste?

Ya hemos establecido el motivo para organizar nuestro código y hacerlo lo más mantenible posible. Ahora podemos comenzar a usar un framework o simplemente nuestros propios ficheros JavaScript. Recuerda que puedes usar cualquier herramienta que pienses que puede ayudarte en esta tarea y que en este caso, he elegido FlightJS por comodidad y haber sido desarrollada y usada por Twitter.

FlightJS

FlightJS es un framework de JavaScript que dispara eventos basados en el comportamiento de los nodos DOM. Para que se entienda mejor, simplemente ejecuta código javascript cuando se encuentra una etiqueta en un elemento o se hace una modificación en el elemento que tiene esa etiqueta.

Este framework para JavaScript necesita JQuery para funcionar y para su instalación, tenéis toda la documentación disponible. Para la implementación, usaremos el concepto de mixin, que no es más que un conjunto de funcionalidad que pueden ser útiles para varios objetos. Es muy parecido a la herencia en JavaScript, de hecho, lo es, pero en este caso, podemos heredar de varios mixins.

Todo esto puede verse mejor con un trozo de código, en este caso, para el logado:

<a id="facebook-login" data-facebook-login="" href="" data-facebook-sdk="//connect.facebook.net/en_GB/sdk.js#xfbml=1&amp;version=v2.6" data-facebook-appid="4545454545">
Login with Facebook
</a>

Este sería un código típico para el logado. En este caso, y como hemos visto en otras ocasiones, estamos usando los objetos de Facebook, en vez del código autogenerado que podríamos tener con las etiquetas de Facebook.

Aparte de eso, lo que nos debe llamar la atención son los atributos que comienzan por “data” en este enlace (también podría ser una etiqueta div…). De estos atributos obtiene los datos y cuando se encuentra Facebook-login ejecuta el código que tengamos asociado a ese componente.

Cada componente tendrá la funcionalidad necesaria para cada acción que se quiera realizar en Facebook: logar, registrar, enviar… mientras que el mixin tendrá la parte común, que es la url y la carga asíncrona del api.

A continuación, se muestra la primera parte de la definición del mixin, donde puedes ver que se utilizará principalmente para crear tres funciones para cargar la SDK asíncronamente mediante la propiedad data-facebook-sdk, que si te fijas bien, se encuentra en el código html que hemos visto anteriormente. El código de las funciones es el mismo que hemos usado en posts anteriores para conectarnos a Facebook


'use strict';

define(
   [
       'mixins/with_options',
       'jquery'
   ],
   function(with_options, $) {

       // return the mixin function
       return withFacebookSdk;

       function withFacebookSdk() {

           this.defaultAttrs({
               facebookSdk: '[data-facebook-sdk]' // optional, default value
           });

           this.loadFacebookSDK = function with_facebook_sdk() {
               var thisFacebookSdk = this.attr.facebookSdk;
               // Load the SDK asynchronously
               (function (d, s, id) {
                   var js, fjs = d.getElementsByTagName(s)[0];
                   if (d.getElementById(id)) return;
                   js = d.createElement(s);
                   js.id = id;
                   js.src = thisFacebookSdk;
                   fjs.parentNode.insertBefore(js, fjs);

               }(document, 'script', 'facebook-jssdk'));
           };


           this.facebookInitConfiguration = function (facebookAppid){
               FB.init({
                   appId: facebookAppid,
                   cookie: true,  // enable cookies to allow the server to access the session
                   xfbml: true,  // parse social plugins on this page
                   version: 'v2.6'
               });
           };

           this.facebookInit = function (facebookAppid, callback) {
               var that = this;
               window.fbAsyncInit = function () {
                   that.facebookInitConfiguration(facebookAppid);
                   callback();
               };
           };

En la segunda parte del mixin se encuentran las funciones que usaremos para el logado y registro, y que como has visto también en anteriores posts, lo único que hacemos es añadirlo en dos funciones ya que al ser genéricas, se pueden reutilizar.


this.loginAndSubmit = function (form, tokenInput, action) {
               var mixin = this;
               FB.getLoginStatus(function(response){
                   // The response object is returned with a status field that lets the
                   // app know the current login status of the person.
                   if (response.status === 'connected') {
                       // Logged into your app and Facebook.
                       mixin.submitAccessToken(response, form, tokenInput, action);
                   } else {
                       // The person is not logged into Facebook, so we're not sure if
                       // they are logged into this app or not.
                       FB.login(function(response) {
                           if (response.status === 'connected') {
                               mixin.submitAccessToken(response, form, tokenInput, action);
                           }
                       }, {scope: 'email'});
                   }
               });
           };

           this.submitAccessToken = function  (response,form,tokenInput,action) {
               var authResponse = response.authResponse;
               if (authResponse) {
                   // The user logged in to the BS App.
                   var loginForm = $(form);
                   $(tokenInput).val(authResponse.accessToken);
                   //form action
                   if (action){
                       loginForm.attr('action', action);
                   }
                   loginForm.submit();
               }
           };
       }

});

Ahora vamos a ver el componente de logado en JavaScript haciendo uso del mixin:


define("ui/facebook_login", ["flight/lib/component", "mixins/with_options", "mixins/with_facebook_sdk"], (function(t, e, n) {
        function i() {
            this.defaultAttrs({
                facebookAppid: "[data-facebook-appid]",
                facebookTokenSelector: "[data-facebook-token]",
                registrationFormSelector: "[data-login-form]",
                events: {
                    click: "click"
                }
            }), this.bindEvent = function() {
                var t = this;
                this.on(this.attr.events.click, (function(e) {
                    e.preventDefault();
                    var n = $(t.attr.registrationFormSelector),
                        i = $(t.attr.facebookTokenSelector);
                    t.loginAndSubmit(n, i, "/process_facebooklogin.htm")
                }))
            }, this.after("initialize", (function() {
                this.collectData();
                var t = this;
                this.facebookInit(this.attr.facebookAppid, (function() {
                    t.bindEvent()
                })), this.loadFacebookSDK()
            }))
        }
        var r = t(i, e, n);
        return r.targetSelector = "[data-facebook-login]", r.targetSelectorModal = void 0, r
    }))

He incluido también el formulario en esta imagen para que puedas de donde se obtiene los datos de los atributos.

En la línea 8 se define el nombre del componente y se indican los mixins que va a usar. Aquí te encuentras con el que se ha creado anteriormente para Facebook.

Con defaultAttrs obtenemos los valores que se encuentran en el formulario html, a través de poner el nombre de los atributos entre corchetes (línea 10).

Mediante bindEvent hemos definido lo que vamos a hacer cuando hagamos click en el enlace. En este caso, se hará un submit del formulario, mediante la llamada a la función loginAndSubmit que tenemos definida en el mixin.

En la línea 25 tenemos la primera función que se ejecuta al hacer click en el enlace, es el punto de entrada al componente y lo primero que se ejecutará, por eso en la línea 26 obtiene los datos del formulario y los carga en los atributos que hemos definido mediante una función. Si te fijas en las líneas 28 y 30 verás las llamadas a las funciones que se han creado en el mixin y que cargan el api de Facebook.

El código para el registro es muy similar y no lo pongo aquí porque sería repetir otra vez lo mismo. Os dejo el del envío de mensajes para que veáis que la base es la misma, siendo éste incluso más sencillo.


'use strict';

define(
   [
       'flight/lib/component',
       'mixins/with_options',
       'mixins/with_facebook_sdk'
   ],

   function (defineComponent, with_options, with_facebook_sdk) {

       var Component = defineComponent(FacebookDialog,with_options,with_facebook_sdk);
       Component.targetSelector = '[data-facebook-send]';
       Component.targetSelectorModal = undefined;

       return Component;

       function FacebookDialog() {

           this.defaultAttrs({
               events: {
                   click: 'click'
               }
           });

           this.bindEvent = function() {

               this.on(this.attr.events.click, function(event) {
                   event.preventDefault();

                   FB.ui({
                       method: 'send',
                       link:  this.attr.href
                   });
               });
           };

           this.after('initialize', function () {
               this.collectData();
               var component = this;
               this.facebookInit(this.attr.facebookAppid,function() {
                   component.bindEvent();
               });
               this.loadFacebookSDK();
           });

       }
   }
);

Conclusiones

En este artículo acabas de ver cómo organizar el código de una forma básica con un framework, en este caso FlightJS. Más que el framework en sí, lo más importante es el concepto de mantenibilidad que tu código adquiere siguiendo un par de ideas y conceptos para que cuando tengas que volver a tocarlo, sea mucho más fácil.

Para comprender que apenas se le han hecho modificaciones al código para adoptar esta solución, te recomiendo que pongas los trozos de código que hemos definido en este artículo y lo compruebes con las versiones usadas anteriormente. Verás que la funcionalidad es la misma y que sólo cambian las cabeceras necesarias para declarar las funciones.

Por último, te recuerdo que hemos realizado una posible implementación y que existen muchas otras que también puede ser correctas, y con otros frameworks. Lo importante es que te sean útiles

Espero que te haya gustado y si tienes alguna pregunta, aportación o quieres que amplíe algo, estoy a tu disposición.


Sobre el autor

Francisco Javier Rodriguez Gallego   

Soy un desarrollador Java en busca de nuevos conocimientos y compartiendo los míos.