Laravel 5, Vue.js y Ajax

Bien, en esta publicación vamos a ver cómo integrar Vue a un proyecto hecho en Laravel. El ejemplo será el siguiente: vamos a administrar una lista de notas con Ajax. Será muy simple, sin embargo llevaremos a cabo una serie de pasos como crear migraciones, compilar un archivo .Vue, usar la librería de vue-resource, etc.

En primer lugar, antes de empezar debemos tener instalado lo siguiente:

  • Composer
  • Node.js

En el caso de Node.js, la instalación del mismo, nos permitirá usar el comando npm. Podés probar en tu computadora, abriendo una terminal (símbolo de sistema en Windows) y escribir lo siguiente:

npm --info

Si te devuelve un error, es porque nunca instalaste Node. Es un buen momento para hacerlo:

https://nodejs.org/es/

Creación del proyecto

Abrimos la consola y creamos nuestro proyecto Laravel:

composer create-project laravel/laravel laravel_vuejs --prefer-dist

Una vez instalado, vamos a la raíz del proyecto y buscamos un archivo llamado: ‘package.json’, donde van las dependencias (‘devDependencies’), agregamos vue y vue-resource:

"vue": "^2.1.10", 
"vue-resource": "^1.3.4"

Probablemente ya exista la primera (vue), de ser así, la dejamos y sólo agregamos ‘vue-resource’

Luego volvemos a la consola y entramos en nuestro proyecto:

cd laravel_vuejs

Y ejecutamos el comando de node que nos va a instalar estas dependencias:

npm install

(Puede tardar bastante, a esperar…)

Modelo y Migración

Primero vamos creamos una base de datos, en mi caso se va a llamar ‘laravel_vuejs’. Luego en el raíz de nuestro proyecto editamos .env:

DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_vuejs
DB_USERNAME=root
DB_PASSWORD=

(Escribimos los datos para conectar a nuestra base de datos)

Volvemos a la consola y escribimos lo siguiente:

php artisan make:migration create_notas_table

Vamos al archivo que acabamos, en database/migrations de crear y le cambiamos los métodos up() y down() por:

public function up() 
{ 
   Schema::create('notas', function(Blueprint $table){ 
      $table->increments('id'); 
      $table->string('descripcion'); 
      $table->timestamps(); 
   }); 
}
public function down() 
{ 
   Schema::dropIfExists('notas'); 
}

Volvemos a la consola y creamos la tabla:

php artisan migrate

Y por último el modelo:

php artisan make:model Nota

 

Controlador y Rutas

Vamos a la consola y creamos el controlador:

php artisan make:controller NotasController

Una vez creado, nos dirigimos al controlador en app/Http/Controllers, y lo editamos con el siguiente código:

use App\Nota;
class NotasController extends Controller  {
   public function notas(){
      return view('notas');  
   }  
   public function mostrarNotas(){
      return Nota::all();  
   }
   public function guardarNota(Request $request){
      $nota = new Nota();
      $nota->descripcion = $request->input('descripcion');
      $nota->save();
      return $this->mostrarNotas();
   }
   public function eliminarNota(Request $request){
      $nota = Nota::find($request->input('id'));
      $nota->delete();
      return $this->mostrarNotas();
   }
}

Analicemos el código. Los métodos:

  • notas(): Carga una vista (GET)
  • mostrarNotas(): Devuelve un json con las notas cargadas (GET)
  • guardarNota(): Recibe una nota, la guarda y devuelve las notas actualizadas (POST)
  • eliminarNota(): Recibe una nota, la elimina y devuelve las notas actualizadas (POST)

Ahora nos queda agregar las rutas en routes/web.php:

Route::get('/notas', 'NotasController@notas'); 
Route::get('/mostrar_notas', 'NotasController@mostrarNotas'); 
Route::post('/guardar_nota', 'NotasController@guardarNota'); 
Route::post('eliminar_nota', 'NotasController@eliminarNota');

 

Vista

Ahora vamos a crear la vista para administrar las notas, dentro de resources/views con el nombre ‘notas.blade.php’,:

<!doctype html> 
<html lang="{{ config('app.locale') }}"> 
 <head> 
 <meta charset="utf-8"> 
 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 
 <meta name="viewport" content="width=device-width, initial-scale=1"> 
 <!-- CSRF Token --> 
 <meta name="csrf-token" content="{{ csrf_token() }}"> 
 <title>Laravel y Vue</title> 
 <link rel="stylesheet" href="{{ elixir('css/app.css') }}"> 
 </head> 
 <body> 
    <div id="app" class="container"> 
       <h1> Notas </h1> 
       <notas></notas> 
    </div> 
    <input type="hidden" id="csrf_token" value="{{ csrf_token() }}" /> 
    <script src="{{ elixir('js/app.js') }}"></script> 
 </body> 
</html>

Vamos a detenernos para entender algunas líneas de este código:

Cargamos una hoja de estilos, esto tiene que ver más con la parte estética, no influye en el funcionamiento:

<link rel="stylesheet" href="{{ elixir('css/app.css') }}">

Incluimos el componente:

<notas></notas>

Aunque aún no lo hemos creado, esta es la forma de incluir componentes. Toda la parte visual de la administración de notas se incluye con las etiquetas con dicho nombre. Etiquetas que por supuesto no son de html, sino inventadas por el desarrollador.

Incluimos un campo de entrada con el token:

<input type="hidden" id="csrf_token" value="{{ csrf_token() }}" />

Aunque las peticiones por POST las haremos con ajax, de cualquier manera tenemos que enviar el token en cada envío, ya que Laravel por cuestiones de seguridad espera por cabecera este dato.

Y por último incluimos el .js base de la aplicación, el cual va a incluir por ejemplo vue, vue-resource y los componentes que vayamos creando:

<script src="{{ elixir('js/app.js') }}"></script>

 

Creando nuestro componente

Antes de continuar vamos a detenernos en algo importante: ¿Qué es un componente? Un componente es código HTML extendido. Va a estar divido en dos partes: una vista y un controlador. La vista es el código HTML, mientras que el controlador es el código Javascript que le da funcionalidad a esa vista.

Creo que a estas alturas la mayoría deben saber de qué se trata, pero si no es así podés verlo de la siguiente forma: imaginate un tablero que contiene una pantalla, botones y palancas. La vista serían justamente esos elementos que en conjunto forman el tablero, pero será el controlador quien por ejemplo guarde la información que vemos  y no vemos por pantalla (datos), o que al pulsar en un botón suceda algo (métodos)

La ventaja del componente es que podemos encapsular un apartado visual y reutilizarlo cada vez que queramos.

Para crear nuestro componente primero tenemos que incluir las librerías, entonces vamos a resources/assets/js/app.js y copiamos el siguiente código:

require('./bootstrap'); 
window.Vue = require('vue'); 
Vue.use(require('vue-resource')); 
Vue.http.headers.common['X-CSRF-TOKEN'] = document.getElementById('csrf_token').value;

Aquí incluimos bootstrap y vue. También vue-resource la librería que nos va a permitir hacer peticiones http. Y finalmente agregamos el token para no tener que preocuparnos en cada petición por POST.

Y más abajo incluimos el componente que aún no hemos creado:

Vue.component('notas', require('./components/Notas.vue'));

Vamos a resources/assets/js/components y creamos un archivo llamado Notas.vue con el siguiente código:

<template> 
   <form> 
      <div class="form-group"> 
         <label> Descripción </label> 
         <input type="text" class="form-control" v-model="nota_temp.descripcion" /> 
      </div> 
      <button type="button" class="btn btn-primary" v-on:click="guardarNota()"> Guardar nota </button> 
      <hr /> 
      <ul> 
         <li v-for="n in notas"> 
            {{ n.descripcion }} 
            | 
            <a href="#" class="text text-danger" v-on:click="eliminarNota(n)"> Eliminar </a> 
         </li> 
      </ul> 
   </form> 
</template> 
 
<script> 
 export default { 
    data(){ 
       return { 
          nota_temp: { 
             descripcion: '' 
          }, 
          notas: [] 
       }
    }, 
    methods: { 
       mostrarNotas(){ 
          this.$http.get('/mostrar_notas').then(response => { 
             this.notas = response.body; 
          }, response => { 
             alert('Error'); 
          }); 
       }, 
       guardarNota(){ 
          this.$http.post('/guardar_nota', { 
             descripcion: this.nota_temp.descripcion 
          }).then(response => { 
             this.nota_temp.descripcion = ''; 
             this.notas = response.body; 
          }, response => { 
             alert('Error'); 
          }); 
       }, 
       eliminarNota(p_nota){ 
          this.$http.post('/eliminar_nota', { 
             id: p_nota.id 
          }).then(response => { 
             this.notas = response.body; 
          }, response => { 
             alert('Error'); 
          }); 
       } 
    },
    created(){ 
       this.mostrarNotas(); 
    } 
 } 
</script>

Finalmente volvemos a la consola y compilamos el componente que acabamos de crear:

npm run dev

(Cada cambio requiere que hagamos esto)

Descargar ejemplo


18 Respuestas a “Laravel 5, Vue.js y Ajax”

  1. Hola Fernando Gaitán.
    Gracias por compartir tus conocimientos.
    Por favor, me puedes ayudar.
    Hice todo igual, pero me emite el error (Instalé cross-env, pero persiste ):
    C:\laragon\www\gaitanlavu>npm run dev

    > @ dev C:\laragon\www\gaitanlavu
    > npm run development

    > @ development C:\laragon\www\gaitanlavu
    > cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js –progress –hide-modules –config=node_modules/laravel-mix/setup/webpack.config.js

    El sistema no puede encontrar la ruta especificada.
    events.js:183
    throw er; // Unhandled ‘error’ event
    ^

    Error: spawn node_modules\webpack\bin\webpack.js ENOENT
    at notFoundError (C:\laragon\www\gaitanlavu\node_modules\cross-spawn\lib\enoent.js:11:11)
    at verifyENOENT (C:\laragon\www\gaitanlavu\node_modules\cross-spawn\lib\enoent.js:46:16)
    at ChildProcess.cp.emit (C:\laragon\www\gaitanlavu\node_modules\cross-spawn\lib\enoent.js:33:19)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:198:12)
    npm ERR! code ELIFECYCLE
    npm ERR! errno 1
    npm ERR! @ development: `cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js –progress –hide-modules –config=node_modules/laravel-mix/setup/webpack.config.js`
    npm ERR! Exit status 1
    npm ERR!
    npm ERR! Failed at the @ development script.
    npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

    npm ERR! A complete log of this run can be found in:
    npm ERR! C:\Users\mleon\AppData\Roaming\npm-cache\_logs\2017-11-30T15_32_41_511Z-debug.log
    npm ERR! code ELIFECYCLE
    npm ERR! errno 1
    npm ERR! @ dev: `npm run development`
    npm ERR! Exit status 1
    npm ERR!
    npm ERR! Failed at the @ dev script.
    npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

    npm ERR! A complete log of this run can be found in:
    npm ERR! C:\Users\mleon\AppData\Roaming\npm-cache\_logs\2017-11-30T15_32_41_567Z-debug.log

    C:\laragon\www\gaitanlavu>

    Saludos cordiales.

    Miguelon
    Quito.-Ecuador

  2. Tu ejemplo lo descargué, Corrí el npm install y salen errores,

    Otro: Vue.http.headers.common[‘X-CSRF-TOKEN’] = document.getElementById(‘csrf_token’).value;
    es valido el .value, porque dispara un error.
    En tu ejemplo no está el file: .env

    1. No, así no, tratá de hacer todos los pasos en tu máquina, yo no puedo ayudarte porque no sé qué contenido tenés en tu máquina.

      Saludos!

  3. Excelente ejemplo!
    Me ayudo mucho a entender un poco mas como usar los componentes de vue.js que se me complicaba un poco al llamarlos.

    Muchas gracias Fernando!

  4. Groso, pero dejame entender ajax donde lo aplicas cuando retornas directamente el metodo o donde esta el ajax alli ?

  5. Hola gran tutorial. Queria preguntarle algo como puedo hacer un buscador de una lista digamos de empleados con vuej y laravel?. Gracias

  6. Hola, gracias por tu aporte, seguí tu artículo para un proyecto que actualmente estoy desarrollando, me ha sido de mucha ayuda.
    He notado que en mi caso en específico, usando Laravel 5.6 me tiraba un error al momento de hacer las peticiones POST/GET y descubrí que faltaba agregar un namespace al controller, el cual es:
    «use Illuminate\Http\Request;»

    Ojalá pueda ser de ayuda para alguien.
    De nuevo muchas gracias

  7. Saludos, gracias por el tutorial estoy iniciando con vuejs en laravel, tengo dudas espero me puedas ayudar.
    – como podria agregar Middlewares a las rutas dependiendo del Role (‘admin’, ‘supervisor’, ‘almacenista’, ‘vendedor’)
    tambien tengo los controladores: ventas, compras, clientes, usuarios, proveedores, usuarios, productos y categorias de productos con sus distintas rutas. anteriormente e colocado las rutas de esta manera Route::resources([ ‘sale’ => ‘SaleController’ ]);
    con vue se podria usar la misma nomenclatura?

    1. No entiendo bien la pregunta. Osea Middleware lo que hace en realidad es filtrar aquellas url que deben ser accedidas por distintos usuarios, por ejemplo usuarios que no están autenticados. Si con vue se intenta consulta con un ajax a una url que necesita que el usuario esté logueado, y éste no lo está, le va a devolver un error.

      Saludos!

  8. buenas, me da este error, cuando lo trato de correr el sistema, y compile, hice todo al cual explican arriba

    (1/1) NotFoundHttpException
    in RouteCollection.php line 179