Como Integrar Vue JS y Ruby on Rails – Parte 2

Demo

En la Parte anterior llamada Como Integrar Vue JS y Ruby on Rails – Parte 1 creamos un nuevo proyecto con Ruby on Rails 6, luego hicimos uso de webpack que viene ya con Ruby on Rails 6 para instalar Vue JS y verificamos si Vue JS se había instalado correctamente haciendo una integración básica mostrando el texto Hello Vue!, en esta parte 2 vamos a crear una tabla HTML con Bootstrap y convertirla en un componente, esto es para ver que ya tenemos Vue JS integrado en Rails y podemos crear cosas al menos un poquito más avanzadas.

Partes


Antes de continuar te invito a leer los siguientes artículos:

Asimismo te invito a escuchar el Podcast: “Con Que Lenguaje De Programación Comenzar Para El Desarrollo Web”:

Spotify: Sound Cloud:

Bien ahora continuemos con el Post: Como Integrar Vue JS y Ruby on Rails – Parte 2.

Componente Postres

Para verificar que podemos crear cosas más allá de un simple mensaje Hello Vue!, recordemos que este mensaje lo mostramos en la Parte 1 de ese tutorial, vamos a mostrar una tabla HTML de Bootstrap 4 que lea datos en formato JSON, los datos JSON que leeré se encuentran en el repositorio GitHub de este proyecto, exactamente en este enlace https://raw.githubusercontent.com/collectivecloudperu/integrar_vuejs26_rubyonrails6/main/postres.json y contiene los siguientes datos.

// https://raw.githubusercontent.com/collectivecloudperu/integrar_vuejs26_rubyonrails6/main/postres.json 
 
[
   {
      "id":"1",
      "nombre":"Torta de Chocolate",
      "stock":"35",
      "precio":"5.00",
      "url":"torta-de-chocolate",
      "img":"https://raw.githubusercontent.com/collectivecloudperu/integrar_vuejs26_rubyonrails6/main/img/tc.jpg",
      "fecha":"2019-06-17 15:56:23"
   },
   {
      "id":"2",
      "nombre":"Pie de Manzana",
      "stock":"45",
      "precio":"4.50",
      "url":"pie-de-manzana",
      "img":"https://raw.githubusercontent.com/collectivecloudperu/integrar_vuejs26_rubyonrails6/main/img/pm.jpg",
      "fecha":"2018-08-11 16:25:33"
   },
   {
      "id":"3",
      "nombre":"Arroz con Leche",
      "stock":"36",
      "precio":"2.50",
      "url":"arroz-con-leche",
      "img":"https://raw.githubusercontent.com/collectivecloudperu/integrar_vuejs26_rubyonrails6/main/img/al.jpg",
      "fecha":"2018-08-11 16:25:36"
   },
   {
      "id":"4",
      "nombre":"Gelatina de Fresa",
      "stock":"60",
      "precio":"1.50",
      "url":"gelatina-de-fresa",
      "img":"https://raw.githubusercontent.com/collectivecloudperu/integrar_vuejs26_rubyonrails6/main/img/gf.jpg",
      "fecha":"2018-08-11 16:25:39"
   },
   {
      "id":"5",
      "nombre":"Suspiro a la Limeña",
      "stock":"26",
      "precio":"6.00",
      "url":"suspiro-a-la-limeña",
      "img":"https://raw.githubusercontent.com/collectivecloudperu/integrar_vuejs26_rubyonrails6/main/img/sl.jpg",
      "fecha":"2019-06-17 15:58:43"
   },
   {
      "id":"6",
      "nombre":"Mazamorra Morada",
      "stock":"50",
      "precio":"3.00",
      "url":"mazamorra-morada",
      "img":"https://raw.githubusercontent.com/collectivecloudperu/integrar_vuejs26_rubyonrails6/main/img/mm.png",
      "fecha":"2018-08-11 16:25:45"
   }
]

Antes de crear mi componente voy a instalar el paquete NPM axios que me permite leer datos en formato JSON, para instalarlo ejecuto el siguiente comando.

# Comando para instalar el paquete 'axios'  
npm install --save axios

+ axios@0.21.0
removed 3 packages, updated 1 package and audited 1124 packages in 7.195s

50 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

Una vez que instalamos axios, debemos de instanciarlo en el archivo hello_vue.js, este archivo se encuentra en app > javascript > packs > hello_vue.js 

/integrar_vuejs26_rubyonrails6
├── /app 
    ├── /assets
    ├── /channels 
    ├── /controllers
    ├── /helpers
    ├── /javascript
        ├── /channels
        ├── /packs
            ├── application.js 
            ├── hello_vue.js Abrimos este Archivo 
        ├── app.vue // 
        ├── Postres.vue 
    ├── /jobs
    ├── /mailers
    ├── /models
    ├── /views
├── /bin
├── /config
├── /db
├── /lib
├── /log
├── /node_modules
├── /public
├── /storage
├── /test
├── /tmp
├── /vendor
├──  .browserslistrc
├──  .gitignore
├──  .ruby-version
├──  babel.config.js
├──  config.ru
├──  Gemfile  
├──  Gemfile.lock
├──  package.json
├──  postcss.config.js
├──  Rakefile
├──  README.md
├──  yarn.lock

Dentro del archivo hello_vue.js creamos una variable global y también una propiedad global para axios. 

/* eslint no-console: 0 */
// Run this example by adding <%= javascript_pack_tag 'hello_vue' %> (and
// <%= stylesheet_pack_tag 'hello_vue' %> if you have styles in your component)
// to the head of your layout file,
// like app/views/layouts/application.html.erb.
// All it does is render <div>Hello Vue</div> at the bottom of the page.

import Vue from 'vue'
import App from '../app.vue'

// Creamos una variable global 'axios' 
window.axios = require('axios');

// Creamos una propiedad global para axios 
Vue.prototype.$axios = axios

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    render: h => h(App)
  }).$mount()
  document.body.appendChild(app.$el)

  console.log(app)
})


// The above code uses Vue without the compiler, which means you cannot
// use Vue to target elements in your existing html templates. You would
// need to always use single file components.
// To be able to target elements in your existing html/erb templates,
// comment out the above code and uncomment the below
// Add <%= javascript_pack_tag 'hello_vue' %> to your layout
// Then add this markup to your html template:
//
// <div id='hello'>
//   {{message}}
//   <app></app>
// </div>


// import Vue from 'vue/dist/vue.esm'
// import App from '../app.vue'
//
// document.addEventListener('DOMContentLoaded', () => {
//   const app = new Vue({
//     el: '#hello',
//     data: {
//       message: "Can you say hello?"
//     },
//     components: { App }
//   })
// })
//
//
//
// If the project is using turbolinks, install 'vue-turbolinks':
//
// yarn add vue-turbolinks
//
// Then uncomment the code block below:
//
// import TurbolinksAdapter from 'vue-turbolinks'
// import Vue from 'vue/dist/vue.esm'
// import App from '../app.vue'
//
// Vue.use(TurbolinksAdapter)
//
// document.addEventListener('turbolinks:load', () => {
//   const app = new Vue({
//     el: '#hello',
//     data: () => {
//       return {
//         message: "Can you say hello?"
//       }
//     },
//     components: { App }
//   })
// })

Bien, ahora creo un archivo llamado Postres.vue que será el archivo para mi componente Postres, en este componente mostraré una lista de postres en una tabla HTML de Bootstrap 4, el archivo Postres.vue lo creamos en app > javascript > Postres.vue

/integrar_vuejs26_rubyonrails6
├── /app 
    ├── /assets
    ├── /channels 
    ├── /controllers
    ├── /helpers
    ├── /javascript
        ├── /channels
        ├── /packs
            ├── application.js 
            ├── hello_vue.js 
        ├── app.vue   
        ├── Postres.vue // Creo y abro este Archivo 
    ├── /jobs
    ├── /mailers
    ├── /models
    ├── /views
├── /bin
├── /config
├── /db
├── /lib
├── /log
├── /node_modules
├── /public
├── /storage
├── /test
├── /tmp
├── /vendor
├──  .browserslistrc
├──  .gitignore
├──  .ruby-version
├──  babel.config.js
├──  config.ru
├──  Gemfile  
├──  Gemfile.lock
├──  package.json
├──  postcss.config.js
├──  Rakefile
├──  README.md
├──  yarn.lock

Abro el archivo Postres.vue y comienzo agregando la tabla HTML en la zona del template de mi componente Postres.

<template>

  <div class="table-responsive">  
    <table class="table mt-3" :items="data" :fields="fields">
      <thead class="thead-dark">
        <tr>
          <th scope="col">Nombre</th>
          <th scope="col">Precio</th>
          <th scope="col">Stock</th>
          <th scope="col">Imagen</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="producto in data" v-bind:key="producto.id">          
          <td>{{ producto.nombre }}</td>
          <td>{{ producto.precio }}</td>
          <td>{{ producto.stock }}</td>
          <td>
            <img v-bind:src="producto.img" class="img-fluid" width="30px" v-bind:alt="producto.nombre">
          </td>
        </tr>
      </tbody>
    </table> 
  </div>

</template>

En el código anterior, si te fijas en la tabla dentro del elemento <tbody></tbody> hago uso de la directiva v-for para listar los datos del archivo JSON. 

<tbody>
   <tr v-for="producto in data" v-bind:key="producto.id">
      <td>{{ producto.nombre }}</td>
      <td>{{ producto.precio }}</td>
      <td>{{ producto.stock }}</td>
      <td>
        <img v-bind:src="producto.img" class="img-fluid" width="30px" v-bind:alt="producto.nombre">
      </td>
   </tr>
</tbody>

Ahora dentro de las etiquetas <script></script> creamos los campos que usaré del JSON, estos son nombre, precio, stock e img. Luego creo data para obtener los datos y pasarlos a la tabla, debajo leemos el JSON con axios. 

<script>

  export default {

    name: 'Postres',

    data: function () {
      return {
        // Campos 
        fields: ['nombre', 'precio', 'stock', 'img'],

        // Obtenemos los datos en el array 'data' 
        data: []
      }
    },

    // Leemos los datos JSON con axios 
    mounted() {
      axios
        .get("https://raw.githubusercontent.com/collectivecloudperu/integrar_vuejs26_rubyonrails6/main/postres.json")
        .then(response => {
            this.data = response.data;
            console.log(this.data); 
        });
    },

  }
</script>

Para que me mi componente Postres funcione, lo debo colocar en el componente app.vue que se creo por defecto luego de instalar Vue JS en Ruby on Rails (RoR), entonces abro el archivo app.vue que se encuentra en app > javascript > app.vue

/integrar_vuejs26_rubyonrails6
├── /app 
    ├── /assets
    ├── /channels 
    ├── /controllers
    ├── /helpers
    ├── /javascript
        ├── /channels
        ├── /packs
            ├── application.js 
            ├── hello_vue.js 
        ├── app.vue // Abro este Archivo 
        ├── Postres.vue 
    ├── /jobs
    ├── /mailers
    ├── /models
    ├── /views
├── /bin
├── /config
├── /db
├── /lib
├── /log
├── /node_modules
├── /public
├── /storage
├── /test
├── /tmp
├── /vendor
├──  .browserslistrc
├──  .gitignore
├──  .ruby-version
├──  babel.config.js
├──  config.ru
├──  Gemfile  
├──  Gemfile.lock
├──  package.json
├──  postcss.config.js
├──  Rakefile
├──  README.md
├──  yarn.lock

Dentro del archivo app.vue coloco en las etiquetas <template></template> el componente Postres para que cargue en la vista.

<template>
  <div id="app">
    <p>{{ message }}</p>

    <!-- Componente Postres -->
    <Postres/>

  </div>
</template>

Y dentro de las etiquetas <script></script> importo el componente Postres, también lo defino dentro de components y en message coloco el texto Lista de Postres.

<script>

  // Importo el componente 'Postres' 
  import Postres from './Postres'

  export default {

    // Defino el componente 'Postres' 
    components: {
      Postres,
    },

    data: function () {
      return {
        message: "Lista de Postres"
      }
    }

  }
</script

Para mi entorno de desarrollo debo ejecutar 2 consolas al mismo tiempo, yo abriré 2 consolas GitBash, en la primera consola ejecuto el servidor de desarroll ode webpack que va a compilar todos los archivos del proyecto mediante el siguiente comando.

./bin/webpack-dev-server 

i 「wdm」: Compiled successfully.
i 「wdm」: Compiling...
i 「wdm」: Hash: c49208c03dcd5e373961
Version: webpack 4.44.2
Time: 331ms
Built at: 23/10/2020 19:50:57
                                     Asset       Size       Chunks                         Chunk Names
    js/application-45c1bcf43c25641d8a80.js    508 KiB  application  [immutable]            application
js/application-45c1bcf43c25641d8a80.js.map    573 KiB  application  [dev]                  application
      js/hello_vue-76a8f7e38e6fe91050ab.js    721 KiB    hello_vue  [emitted] [immutable]  hello_vue
  js/hello_vue-76a8f7e38e6fe91050ab.js.map    807 KiB    hello_vue  [emitted] [dev]        hello_vue
                             manifest.json  689 bytes               [emitted]   
i 「wdm」: Compiled successfully.

Y en la segunda consola ejecuto el servidor de Ruby on Rails 6 (RoR) con el siguiente comando.

rails server 

=> Booting Puma
=> Rails 6.0.3.4 application starting in development
=> Run `rails server --help` for more startup options
*** SIGUSR2 not implemented, signal based restart unavailable!
*** SIGUSR1 not implemented, signal based restart unavailable!
*** SIGHUP not implemented, signal based logs reopening unavailable!
Puma starting in single mode...
* Version 4.3.6 (ruby 2.7.2-p137), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://[::1]:3000
* Listening on tcp://127.0.0.1:3000
Use Ctrl-C to stop
Started GET "/" for ::1 at 2020-10-23 19:51:37 -0500
   (2.8ms)  SELECT sqlite_version(*)
Processing by AppController#index as HTML
  Rendering app/index.html.erb within layouts/application
  Rendered app/index.html.erb within layouts/application (Duration: 2.1ms | Allocations: 258)
Completed 200 OK in 44ms (Views: 33.6ms | ActiveRecord: 0.0ms | Allocations: 7622)

Si voy a la dirección local http://localhost:3000/ en el navegador, debo de ver los datos sin problemas.

Con esto hemos realizado algo más que un simple mensaje Hello Vue!, recordemos que este mensaje lo mostramos en la Parte 1 de ese tutorial.

Bien hasta aquí terminamos esta primera parte en donde creamos un componente llamado Postres para listar datos JSON en una tabla HTML.

Ten Paciencia, lo que quiero es que conozcas bien como crear este proyecto y no llenarte el capitulo de mucho contenido porque te puedes marear y no tendrás un óptimo aprendizaje. 

Nota (s)

  • En la 3ra parte de este tutorial vamos a estilizar la interface agregando una tabla HTML de Bootstrap 4 y otros detalles.
  • No olvides que debemos usar la Tecnología para hacer cosas Buenas por el Mundo. 

Síguenos en nuestras Redes Sociales para que no te pierdas nuestros próximos contenidos.