Hoy vamos a ejercer nuestras capacidades con Vue.js desarrollando una aplicación muy sencilla para navegar entre los mensajes de reddit. Vamos a desarrollarlo todo desde cero para demostrar lo fácil que es crear interfaces de usuario con un framework como Vue.
Este tutorial requiere que tengas al menos ciertos conocimientos básicos sobre JavaScript y Vue.js. Si no estás familiarizado con Vue.js en absoluto, te recomendamos que le eches un vistazo a nuestro artículo 4 ejemplos prácticos para aprender Vue.js, donde te mostramos muchos de los conceptos básicos mediante unos cuantos snippets orientativos.
La App
Lo que queremos que haga nuestra aplicación es que busque el feed de un número de subreddits y los muestre. Así es como debería verse el resultado final.
Tendremos seis feeds de subreddits separados, mostrando cinco posts cada uno. Los posts tendrán links al contenido y a la discusión en reddit, así como otros detalles. En aras de la simplicidad se han omitido características tales como la inserción/eliminación de subreddits y las búsquedas, pero con la aplicación existente, es muy sencillo realizarlo en un futuro.
Configuración del espacio de trabajo
Antes de entrar de lleno en el código, vamos a asegurarnos de que todo está configurado correctamente. He aquí un resumen de la estructura de archivos:
Como puedes ver, es bastante básico: sólo tenemos un archivo HTML, un archivo CSS, un script.js que contendrá nuestro propio código JavaScript. También hemos añadido copias locales de Vue.js y las librerías de Vue-resource, pero puedes utilizar un CDN, si lo prefieres.
Afortunadamente, Vue.js no requiere ninguna configuración especial, así que debería funcionar directamente. Para iniciar la aplicación sólo tenemos que crear una instancia global Vue:
new Vue({ el: 'body' });
Lo único que queda por hacer ahora es iniciar el servidor web local para habilitar las solicitudes AJAX a la API de reddit. La forma más sencilla de hacerlo en OS X/Ubuntu es mediante la ejecución del siguiente comando desde el directorio del proyecto:
python -m SimpleHTTPServer 8080
Si has seguido todo al pie de la letra, nuestro proyecto deberá estar disponible en localhost: 8080.
Creación de componentes personalizados
Nuestra aplicación va a necesitar dos componentes reutilizables: uno para los posts, y otro para los subreddits. Los dos componentes mantendrán una relación padre-hijo, lo que significa que el componente subreddit tendrá múltiples mensajes anidados en él.
Vamos a empezar con el componente subreddit, y concretamente, con su JavaScript:
// Parent | Subreddit component containing a list of 'post' components. var subreddit = Vue.component('subreddit',{ template: '#subreddit', props: ['name'], data: function () { return { posts: [] } }, created: function(){ this.$http.get("https://www.reddit.com/r/"+ this.name +"/top.json?limit=5") .then(function(resp){ this.posts=resp.data.data.children; }); } });
Aquí definimos el nuevo componente con el nombre subreddit. En props proporcionamos un array con todos los parámetros que nuestro componente pueda recibir. En este caso, es sólo el nombre del subbreddit que queremos ver. Ahora bien, si queremos añadir un bloque subreddit al HTML, usaremos el siguiente código:
<subreddit name="food"></subreddit>
La propiedad data define qué variables son necesarias para cada instancia del componente y sus valores por defecto. Vamos a empezar con un array de posts vacío, para rellenarla con el método creado. Cuando se crea una etiqueta subreddit, Vue tomará su propiedad name, hará una llamada a la API de reddit para buscar los 5 primeros posts del subreddit con ese nombre, y los guardará en this.posts. Para las peticiones HTTP utilizaremos la librería vue-resource en lugar de jQuery, ya que es mucho más pequeña y, automáticamente, cuenta con contexto correcto para this.
Después de adquirir todo lo que necesitamos en el modelo, Vue.js automáticamente renderizará nuestros componentes subreddit. La vista actual que ve el usuario se define en una plantilla llamada index.html:
<template id="subreddit"> <div class="subreddit"> <h2>{{ name | uppercase }}</h2> <ul class="item-list"> <li v-for="obj in posts"> <post :item="obj"></post> </li> </ul> </div> </template>
Personalmente, me gusta meter todos los elementos de un componente en un contenedor div. Esto hace que sea más sencilla darle estilo y también hace que sea más semántico (al menos para mí). Dentro de ese contenedor, tenemos un título (el filtro uppercase viene incorporado en Vue) y una lista desordenada iterando sobre los elementos devueltos por la llamada a la API de reddit.
Si te fijas bien en el código HTML, habrás notado que estamos usando la etiqueta post. Este no es un nuevo elemento en HTML, es nuestro componente hijo.
// Child | Componenet represiting a single post. var post = Vue.component('post', { template: "#post", props: ['item'] });
Los componentes post esperarán un objeto llamado item que contiene toda la información acerca de un solo post en reddit, cosas como el título, la URL, el número de comentarios, etc. Como hemos visto anteriormente, esto se hace en un v-for dentro del componente padre del subreddit:
<li v-for="obj in posts"> <post :item="obj"></post> </li>
La prefijación de dos punto :item = "obj" es muy importante. Indica a Vue que estamos otorgando un objeto JavaScript llamado obj, lo que nos permite pasar los datos del v-for.
Ahora que tenemos todas las propiedades necesarias para un post, podemos mostrarlos. La plantilla puede dar miedo al principio, pero en realidad no es nada del otro mundo:
<template id="post"> <div class="post"> <a :href="item.data.url" :style="item.data.thumbnail | setAsBackground" target="_blank" class="thumbnail"></a> <div class="details"> <a :href="item.data.url" :title="item.data.title" target="_blank" class="title"> {{ item.data.title | truncate}} </a> <div class="action-buttons"> <a href="http://reddit.com{{ item.data.permalink }}" title="Vote"> <i class="material-icons">thumbs_up_down</i> {{item.data.score}} </a> <a href="http://reddit.com{{ item.data.permalink }}" title="Go to discussion"> <i class="material-icons">forum</i> {{item.data.num_comments}} </a> </div> </div> </div> </template>
La único que me falta mencionar aquí es el uso de los filtros setAsBackground y truncate. En contraste con el filtro uppercase que hemos utilizado anteriormente, no vienen en Vue y los tuvimos que hacer nosotros mismos.
Pero eso lo dejaremos para un artículo más adelante. Espero que os haya gustado este sencillo tutorial.
Fuente: tutorialzine.com