Aplicación CRUD con Vue.js y Codeigniter, Segunda parte

Bueno, continuando con nuestro ejemplo de cómo realizar un sistema CRUD con Vue.js y Codeigniter, vamos a finalizar mostrando cómo desarrollar la funcionalidad del frontend.

Primero nos dirigimos a application/views/tareas, abrimos el archivo «index.php» y vamos a agregar el siguiente código:

<html>
<head>
   <meta charset="utf-8" />
   <title> Lista de tareas </title>
   <link rel="stylesheet" type="text/css" href="<?php echo base_url() ?>css/bootstrap.min.css" />
   <link rel="stylesheet" type="text/css" href="<?php echo base_url() ?>css/bootstrap-responsive.min.css" />
</head>
<body>
   <div class="container">
      <h1> Lista de tareas </h1>
      <form action="javascript:void(0);" id="controlador_tareas">
         <div v-if="cargando_tareas">
            Cargando lista de tareas...
         </div>
         <table class="table" v-if="!cargando_tareas">
            <thead>
               <tr>
                  <th> Título </th>
                  <th> Descripción </th>
                  <th> Estado </th>
                  <th> </th>
               </tr>
            </thead>
            <tbody>
               <!-- Fila para modificar una tarea. -->
               <tr v-for="tar in tareas">
                  <td>
                     <input type="text" v-model="tar.titulo" />
                  </td>
                  <td>
                     <textarea v-model="tar.descripcion"></textarea>
                  </td>
                  <td>
                     <select v-model="tar.id_estado" v-bind:class="colorEstado(tar)">
                        <option v-for="est in estados" v-bind:value="est.id_estado"> {{ est.nombre }} </option>
                     </select>
                  </td>
                  <td>
                     <button class="btn btn-success" v-on:click="modificarTarea(tar)"> Guardar </button>
                     <button class="btn btn-danger" v-on:click="eliminarTarea(tar)"> Eliminar </button>
                  </td>
               </tr>
               <!-- Fin Fila para modificar una tarea. -->
               <!-- Fila para guardar una nueva tarea. -->
               <tr>
                  <td>
                     <input type="text" v-model="tarea_nueva.titulo" />
                  </td>
                  <td>
                     <textarea v-model="tarea_nueva.descripcion"></textarea>
                  </td>
                  <td>
                     &nbsp;
                  </td>
                  <td>
                     <button class="btn btn-success" v-on:click="crearTarea()"> Guardar </button>
                  </td>
               </tr>
               <!-- Fin Fila para guardar una nueva tarea. -->
            </tbody>
         </table>
      </form>
   </div>
   <script type="text/javascript" src="<?php echo base_url() ?>js/vue.js"></script>
   <script type="text/javascript" src="<?php echo base_url() ?>js/vue-resource.min.js"></script>
   <script type="text/javascript" src="<?php echo base_url() ?>js/app.js"></script>
</body>
</html>

Más adelante voy a explicar este código, continuemos…

Css y Js

Vamos a ir a Twiter Bootstrap, y a descargarnos dicha librería, sólo los Css nos servirán de utilidad para darle un poco de estilo a nuestro proyecto:

https://getbootstrap.com/2.3.2/

Los cuales fueron incluidos en el código anterior:

<link rel="stylesheet" type="text/css" href="<?php echo base_url() ?>css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="<?php echo base_url() ?>css/bootstrap-responsive.min.css" />

Descargamos vue.js desde su web:

https://vuejs.org/v2/guide/installation.html

Y también incluimos en el código la librería:

<script type="text/javascript" src="<?php echo base_url() ?>js/vue.js"></script>

Otra cosa, vamos a necesitar otra librería que nos permita hacer peticiones http (ajax). La descargamos desde:

https://cdn.jsdelivr.net/vue.resource/1.3.1/vue-resource.min.js

Y la importamos:

<script type="text/javascript" src="<?php echo base_url() ?>js/vue-resource.min.js"></script>

Por últimos vamos a crear un archivo .js, el cual yo voy a llamar app.js, y va a tener el siguiente código:

var controlador_tareas = new Vue({
   el: '#controlador_tareas',
   data: {
      cargando_tareas: true,
      tarea_nueva: {
         titulo: '',
         descripcion: ''
      },
      estados: [],
      tareas: []
   },
   methods: {
      recuperarEstados: function(){
         this.$http.get('recuperar_estados').then(function(respuesta){
            this.estados = respuesta.body;
         }, function(){
            alert('No se han podido recuperar los estados.');
         });
      },
      recuperarTareas: function(){
         this.cargando_tareas = true;
         this.$http.get('recuperar_tareas').then(function(respuesta){
            this.tareas = respuesta.body;
            this.cargando_tareas = false;
         }, function(){
            alert('No se han podido recuperar los estados.');
            this.cargando_tareas = false;
         }); 
      },
      crearTarea: function(){
         this.$http.post('crear_tarea', this.tarea_nueva).then(function(){
            this.tarea_nueva.titulo = '';
            this.tarea_nueva.descripcion = '';
            this.recuperarTareas();
         }, function(){
            alert('No se ha podido crear la tarea.');
         });
      },
      modificarTarea: function(p_tarea){
         this.$http.post('modificar_tarea', p_tarea).then(function(){
            this.recuperarTareas();
         }, function(){
            alert('No se ha podido modificar la tarea.');
         });
      },
      eliminarTarea: function(p_tarea){
         this.$http.post('eliminar_tarea', p_tarea).then(function(){
            this.recuperarTareas();
         }, function(){
            alert('No se ha podido eliminar la tarea.');
         });
      },
      colorEstado: function(p_tarea){
         var estilo;
         switch(p_tarea.id_estado){
            case '1':
               estilo = 'text text-error';
               break;
            case '2':
               estilo = 'text text-warning';
               break;
            case '3':
               estilo = 'text text-success';
               break;
            default:
               estilo = 'text text-info';
         }
         return estilo;
      }
   },
   created: function(){
      this.recuperarEstados();
      this.recuperarTareas();
   }
});

Y lo incluimos:

<script type="text/javascript" src="<?php echo base_url() ?>js/app.js"></script>

Relación View-Controller

Bueno, finalmente vamos a hacer una explicación de la instancia de Vue que creamos, y cómo funciona en base al código html creado anteriormente:

En primer lugar tenemos un atributo «cargando_tareas», que va a guardar un boolean indicando a la vista si se está esperando una respuesta del servidor, para así de esta forma mostrar u ocultar un texto de pre carga, y así hacer lo propio con la lista de tareas:

cargando_tareas: true

Y en código html:

<div v-if="cargando_tareas"> Cargando lista de tareas... </div>

Cada vez que hagamos un refresco para mostrar la nueva lista de tareas, nos mostrará este mensaje en pantalla, hasta que por fin podamos ver la lista actualizada, la pre carga va a estar oculta.

También tenemos un atributo que tendrá una suerte de tarea temporal, y va a servir para guardar las nuevas tareas que vamos creando.

tarea_nueva: {
   titulo: '',
   descripcion: ''
}

E impactará en la vista de esta forma:

<!-- Fila para guardar una nueva tarea. -->
<tr>
    <td>
       <input type="text" v-model="tarea_nueva.titulo" />
    </td>
    <td>
       <textarea v-model="tarea_nueva.descripcion"></textarea>
    </td>
    <td>
       &nbsp;
    </td>
    <td>
       <button class="btn btn-success" v-on:click="crearTarea()"> Guardar </button>
    </td>
</tr>
<!-- Fin Fila para guardar una nueva tarea. -->

Podemos ver la realación de los atributos de «tarea_nueva» con sus atributos:

<input type="text" v-model="tarea_nueva.titulo" />

Este atributo será enviado el servidor para guardar una nueva tarea con el método «crearTarea()», el cual se llama al pulsar en el botón:

crearTarea: function(){
   this.$http.post('crear_tarea', this.tarea_nueva).then(function(){
      this.tarea_nueva.titulo = '';
      this.tarea_nueva.descripcion = '';
      this.recuperarTareas();
   }, function(){
      alert('No se ha podido crear la tarea.');
   });
},

Este método va a enviar un json con el título y la descripción de la nueva tarea. Una vez que el servidor nos responda indicando que la tarea se creó, el atributo «tarea_nueva» se limpian los datos título y descripción.

Los estados de las tareas se guardarán en un atributo «estados»:

estados: []

Este atributo va llenarse mediante el método: «recuperarEstados()»:

recuperarEstados: function(){
   this.$http.get('recuperar_estados').then(function(respuesta){
      this.estados = respuesta.body;
   }, function(){
      alert('No se han podido recuperar los estados.');
   });
}

Una vez que el servidor nos responda obtendremos algo como esto:

[{"id_estado":"1","nombre":"Pendiente"},{"id_estado":"2","nombre":"En proceso"},{"id_estado":"3","nombre":"Finalizada"},{"id_estado":"4","nombre":"Cancelada"}]

Estos datos serán impactados en los combos para mostrar los estados:

<select v-model="tar.id_estado" v-bind:class="colorEstado(tar)">
   <option v-for="est in estados" v-bind:value="est.id_estado"> {{ est.nombre }} </option>
</select>

Mediante v-for que nos permite recorrer una array, repitiendo el código html que se va a dibujar en cada vuelta.

También tendremos el atributo «tareas»:

tareas: []

Y se llenará mediante el método "recuperarTareas()":

recuperarTareas: function(){
   this.cargando_tareas = true;
   this.$http.get('recuperar_tareas').then(function(respuesta){
      this.tareas = respuesta.body;
      this.cargando_tareas = false;
   }, function(){
      alert('No se han podido recuperar los estados.');
      this.cargando_tareas = false;
   }); 
}

 

El atributo «tareas» a diferencia de «estados», va a cargarse varias veces durante el funcionamiento de la aplicación. Sin embargo, dentro del código html no tendremos que hacer refrescos constantes, cada vez que el atributos cambie su valor Vue se encargará de dibujar el código html nuevamente:

<!-- Fila para modificar una tarea. -->
<tr v-for="tar in tareas">
   <td>
      <input type="text" v-model="tar.titulo" />
   </td>
   <td>
      <textarea v-model="tar.descripcion"></textarea>
   </td>
   <td>
      <select v-model="tar.id_estado" v-bind:class="colorEstado(tar)">
         <option v-for="est in estados" v-bind:value="est.id_estado"> {{ est.nombre }} </option>
      </select>
   </td>
   <td>
      <button class="btn btn-success" v-on:click="modificarTarea(tar)"> Guardar </button>
      <button class="btn btn-danger" v-on:click="eliminarTarea(tar)"> Eliminar </button>
   </td>
</tr>
<!-- Fin Fila para modificar una tarea. -->

Los métodos «recuperarEstados()» y «recuperarTareas()» va a ejecutarse ni bien se haya terminado de crear la instancia de Vue, con el método «created()»:

created: function(){
   this.recuperarEstados();
   this.recuperarTareas();
}

En el código html también podemos observar dos métodos que se llaman con el evento v-on:click. «modificarTarea()»:

modificarTarea: function(p_tarea){
      this.$http.post('modificar_tarea', p_tarea).then(function(){
      this.recuperarTareas();
   }, function(){
      alert('No se ha podido modificar la tarea.');
   });
}

y «eliminarTarea()»:

eliminarTarea: function(p_tarea){
   this.$http.post('eliminar_tarea', p_tarea).then(function(){
      this.recuperarTareas();
   }, function(){
      alert('No se ha podido eliminar la tarea.');
   });
}

En ambos casos enviamos la tarea modificada al servicio, y una vez que nos responda el servidor, volvemos a cargar la lista de tareas con el método «recuperarTareas()».

Cada tarea que obtenemos cuando recorremos el for, la agregamos a los parámetros de modificar y eliminar, en definitiva son json con el siguiente aspecto:

{id_tarea: "7", titulo: "Subir tutorial de Vue.js", descripcion: "Escribiendo POST.", id_estado: "2"}

Bueno, hasta acá todo. Para los que quieran descargarse el código, aquí se encuentra, en mi repositorio:

https://github.com/fernandoggaitan/vuejs_codeigniter

Saludos y feliz día del trabajador!

Ver parte 1


16 Respuestas a “Aplicación CRUD con Vue.js y Codeigniter, Segunda parte”

  1. Excelente artículo. Estaba buscando algo como esto, donde se usa codeigniter con Vuejs. Me gustaría que escribiera más artículos en casos donde los formularios sean más complejos.

  2. Excelente articulo Fernando. Te felicito y te animo a seguir escribiendo.
    Hace años que uso CI y me gustaria poder integrarlo con Vue.

    Gracias por tomarte el tiempo en escribir.

  3. A esto le llamo programar en español, fantástico aporte!! y de gran ayuda para los que estamos mareados con tanto framework que anda por ahí, creo que esto que hiciste me aclaro mucho el panorama gracias!!! honestamente a codeigniter lo tenia pero vue es nuevo para mi hay que entrarle si o si.

  4. ¿Se puede instalar codeigniter con vue usando laravel-mix y así usar el watcher de webpack para agilizar el desarrollo?

  5. Hey q tal saludos. Acabo de pescar a codeigniter pero ahora que escribo terminándose el 2018 por dónde creen que va la cosa: Codeigniter o Laravel? Vue Angular o React? Y con phyton y django que sucede?

    1. Codeigniter o Laravel? Si podés aprender Laravel, mejor. Hay mucha oferta laboral por ahí, aunque Codeigniter puede ayudarte a hacer proyectos propios y también se usa en algunas empresas

      Vue, Angular o React? A mí me gusta bastante Vue, pero es sólo una opinión personal.

      Y con phyton y django que sucede? Lo mismo que Laravel, tiene bastante oferta laboral.

      Saludos!

  6. Falta cargar el helper url en el autoload.php para evitar el error con base_url:

    $autoload[‘helper’] = array(‘url’);

    Excelente tutorial, muchas gracias