Tutorial. Buscador de gifs con Giphy y Vue.js

12/08/2016

Bueno chicos, hoy realizaremos un buscador de gifs usando exclusivamente Vue.js, un framework que a diferencia de jQuery nos permite poner javascript en el propio DOM evitándonos selecciones innecesarias. Para hacerlo más interesante he contado con la colaboración de la Api Giphy, bastante popular tras su integración en twitter.

API Giphy nos permite obtener gifs de manera muy cómoda. Mas info: api.giphy.com

Lo primero será crear una carpeta con tan solo 3 archivos. El primero sera bower.json que contendrá las dependencias vue (sin esto no vamos a ningún sitio), vue-resource para hacer las peticiones a la API Giphy y Font-awesome para usar su iconografía:

{
  "name": "Vuejs - Tutorial",
  "description": "Giphy with Vuejs",
  "main": "",
  "license": "MIT",
  "homepage": "",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "vue-resource": "^0.9.3",
    "vue": "^1.0.26",
    "font-awesome": "fontawesome#^4.6.3"
  }
}

Pasamos al package.json. Me habría gustado haberlo unificado todo en el bower pero vue-infinite-scroll no se encontraba disponible. Aquí instalaremos nuevamente vue, firebase y vuefire (estos dos últimos para crear un histórico de búsquedas):

{
  "name": "vuejs",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "firebase": "^2.4.1",
    "vue": "^1.0.26",
    "vue-infinite-scroll": "^0.2.3",
    "vuefire": "^1.2.0"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Y finalmente el index.html, un fichero con los scripts & estilos necesarios ya configurados para solo preocuparnos de los estilos, el DOM y el script como veréis a continuación:

<html lang="" id="giphy"> <head> <meta charset="UTF-8"> <meta name="viewport"
    content="width=device-width, initial-scale=1.0"> <title>SEARCH GIFS</title> <link rel="stylesheet"
    href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <link rel="stylesheet"
    href="bower_components/font-awesome/css/font-awesome.min.css"> /* Aqui pondremos los estilos */ </head>
    <body> /* Aqui tendremos el DOM */ <script src="bower_components/vue/dist/vue.min.js"> <script
    src="bower_components/vue-resource/dist/vue-resource.min.js"> <script
    src="node_modules/vue-infinite-scroll/vue-infinite-scroll.js"> <script
    src="node_modules/firebase/lib/firebase-web.js"> <script src="node_modules/vuefire/dist/vuefire.min.js"> /*
    Aqui ira el script de Vue.js */ </body> </html>

Con estos tres ficheros ya tendremos todo lo necesario para tener montado nuestro entorno. Tan solo nos queda descargarnos los componentes con un simple comando:

npm install && bower install

Nuestro buscador de gifs tendrá un input para buscar por palabras, un select para limitar el número de gifs que se muestran, un botón para buscar y finalmente, otro para reproducir/pausar todos los gifs. Debajo del formulario se colocarán pequeños botones indicándonos las búsquedas realizadas junto a un grid donde se visualizarán los gifs en formato 16:9. Teniendo en cuenta todo esto, nuestro DOM tendrá el siguiente aspecto:

<div class="container form"> /* Formulario de busqueda */ <form class="row"> <div class="form-group
    col-xs-12 col-sm-6 col-md-3"> <input type="text" class="form-control" placeholder="SEARCH"> </div>
    <div class="form-group col-xs-12 col-sm-6 col-md-3"> <select class="form-control">
    <option>32</option> <option>64</option> <option>128</option> </select>
    </div> <div class="form-group col-xs-6 col-md-3"> <a class="btn btn-block
    btn-primary">SEARCH</a> </div> <div class="form-group col-xs-6 col-md-3"> <a class="btn
    btn-block btn-primary"> <i class="fa fa-play"></i></a> </div> </form> <div
    class="col-xs-12 row record"> <button class="btn btn-default">Star Wars X</button> </div>
    </div> /* Gifs encontrados */ <div class="container grid"> <div class='col-xs-6 col-sm-6 col-md-3
    col-lg-3'> <a><img></a> </div> </div> </code> </pre>

Ahora es cuando tendremos que darle de vidilla y conseguir que cumpla con su cometido de encontrarnos esos gifs que tanto ansiamos. Por ejemplo, el input, la variable/modelo word recogerá el valor que contiene el input en todo momento:

/* AFTER */ <div class="form-group col-xs-12 col-sm-6 col-md-3"> <input type="text" class="form-control"
    placeholder="SEARCH"> </div> /* BEFORE with Vue.js */ <div class="form-group col-xs-12 col-sm-6
    col-md-3"> <input type="text" v-model="word" class="form-control" placeholder="SEARCH"> </div>
    </code> </pre>

Pasamos al select. Tendrá 4 elementos (32,64,128,256) almacenados en una variable limits en forma de lista que detectaran cualquier cambio almacenando el valor de la opción selecciona en selected:

/* AFTER */
<div class="form-group col-xs-12 col-sm-6 col-md-3">
  <select class="form-control">
    <option>32</option>
    <option>64</option>
    <option>128</option>
  </select>
</div>

/* BEFORE with Vue.js */

<div class="form-group col-xs-12 col-sm-6 col-md-3">
  <select class="form-control" v-model="selected">
    <option v-for="item in limits">{% raw />{{item}}{% endraw /></option>
  </select>
</div>

El botón de búsqueda SEARCH lanzara el método search cada vez que sea clickeado. No tiene ningún misterio al respecto:

/* AFTER */
<div class="form-group col-xs-6 col-md-3">
  <a
    class="btn btn-block
    btn-primary"
    >SEARCH</a
  >
</div>
/* BEFORE with Vue.js */
<div class="form-group col-xs-6 col-md-3">
  <a class="btn btn-block btn-primary" v-on:click="search"> SEARCH </a>
</div>

El botón de reproducir/pausar gifs. Por cada click, se ejecutará la funcion play_gifs que modificara la variable play el icono del mismo según la variable play:

/* AFTER */
<div class="form-group col-xs-6 col-md-3">
  <a class="btn btn-block btn-primary">
    <i class="fa fa-play"></i>
  </a>
</div>

/* BEFORE with Vue.js */

<div class="form-group col-xs-6 col-md-3">
  <a class="btn btn-block btn-primary" v-on:click="play_gifs">
    <i v-bind:class="{ 'fa fa-pause': play, 'fa fa-play': !play}"> </i>
  </a>
</div>

Finalmente llegamos al grid, que consta de un bucle de gifs( variable - lista) que contendrá imágenes con javascript. Por un lado contralaremos la reproducción de los gifs según se posicione el cursor encima de estos con mouseenter y mouseleave. ¿Os acordáis de la variable play? pues es la encargada de cambiar el src y alternar entre la imagen y el gif animado:

/* BEFORE with Vue.js */
<div class="container grid">
  <div
    v-for="gif in gifs"
    class="col-xs-6 col-sm-6
    col-md-3 col-lg-3"
  >
    <img
      v-on:mouseenter="entrar(gif)"
      v-on:mouseleave="salir(gif)"
      :src="play ?
    gif.images.fixed_height_small.url : gif.images.fixed_height_small_still.url"
    />
  </div>
</div>

Bueno, dejémonos de rodeos y pongámonos a programar de verdad.

Ahora pasamos al javascript: html tenía un identificador giphy que necesitara el para posicionarse, las variables se colocan en data y la funciones en methods:

new Vue({
  /* seleccionas el DOM */
  el: '#giphy',
  /* las variables mencionadas anteriormente. Se ha incluido aux
    para salvaguardar una url cuando hagamos hover sobre un gif */
  data: {
    word: '',
    gifs: [],
    selected: 32,
    limits: [32, 64, 128],
    gifs: [],
    play: false,
    aux: '',
  },

  /* En cuanto cargamos la web, mostramos gifs */
  ready: function () {
    this.getGifs()
  },

  methods: {
    /* toggle que modifica la variable booleana play */
    play_gifs: function () {
      this.play = !this.play
    },

    /* Si el cursor pasa por encima de un gif modifica la url */
    entrar: function (gif) {
      this.aux = gif.images.fixed_height_small_still.url
      gif.images.fixed_height_small_still.url = gif.images.fixed_height_small.url
    },

    /* Si el cursor sale de un gif restaura el gif en pausa */
    salir: function (gif) {
      gif.images.fixed_height_small_still.url = this.aux
    },

    /* funcion de busqueda */
    search: function () {
      this.getGifs()
    },

    getGifs: function () {
      /* Si la palabra no es vacia, buscamos gifs gracias a la variable word y limitado por limit */
      if (this.word != '') {
        this.$http
          .get(
            'http://api.giphy.com/v1/gifs/search?q=' +
              this.word +
              '&api_key=dc6zaTOxFJmzC&limit=' +
              this.selected.toString(),
          )
          .then(
            (response) => {
              this.$set('gifs', response.data.data)
            },
            (response) => {},
          )
      } else {
        /* En caso contrario, mostramos los gifs que sean trending topic */
        this.$http
          .get('http://api.giphy.com/v1/gifs/trending?api_key=dc6zaTOxFJmzC&limit=' + this.selected.toString())
          .then(
            (response) => {
              this.$set('gifs', response.data.data)
            },
            (response) => {},
          )
      }
    },
  },
})

Ha quedado chulo aunque se podría mejorar.

Lamentablemente me he dejado en el tintero el uso de componentes como son la modal o el uso de firebase pero lo actualizare cuando disponga de algo de tiempo. Os dejo una copia del proyecto por si interesa.