El artículo anterior nos hizo construir una API JSON Rest usando Laravel Framework. En este tutorial, usaremos Vuejs para crear una interfaz que pueda consumir la API que tenemos instalada. En lugar de representar archivos blade, podemos usar componentes de Vue y AJAX para simplemente obtener los datos que necesitamos mostrar desde la API. Incluso aprenderemos un poco sobre cómo personalizar el CSS en Laravel usando SCSS y Laravel Mix.
Instalación de dependencias
Para comenzar, necesitamos instalar las dependencias especificadas en el archivo package.json de nuestra instancia de Laravel. Para hacer esto, simplemente ejecute npm install
en la raíz del proyecto así.
Esto descargará e instalará todo el software necesario para la parte frontal del desarrollo.
Mezcla de Laravel
Laravel Mix es una de las dependencias que ahora está instalada. Webpack es realmente difícil. Laravel Mix lo hace más fácil. Con Mix y SCSS podemos cambiar rápidamente el aspecto del sitio. Antes de ejecutar Mix, debemos comprender qué va a hacer por nosotros. Entonces, de forma predeterminada, Mix sigue la lógica webpack.mix.js
que se encuentra en el directorio raíz de un proyecto de Laravel.
| const mix = require('laravel-mix'); /* |-------------------------------------------------------------------------- | Mix Asset Management |-------------------------------------------------------------------------- | | Mix provides a clean, fluent API for defining some Webpack build steps | for your Laravel application. By default, we are compiling the Sass | file for the application as well as bundling up all the JS files. | */ mix.js('resources/js/app.js', 'public/js') .sass('resources/sass/app.scss', 'public/css'); |
Sin embargo, ¿qué significa esto? Bueno, cuando ejecute, npm run dev
por ejemplo, Mix verá el contenido de resources / js / app.js y resources / sass / app.scss . Luego, compilará estos activos sin procesar en código utilizable colocado en public / js y public / css .
¿Cómo personalizo mis estilos?
Digamos que quieres probar el genial tema Minty Bootswatch en un Proyecto Laravel. ¿Cómo podemos hacer eso con Mix? ¡Muy fácil! Descargar el _variables.scss archivo desde Bootswatch sitio y reemplazar la que se encuentra en /resources/sass/_variables.scss
. Ahora vemos el archivo original y el nuevo.
_Variables.scss originales
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // Body $body-bg: #f8fafc; // Typography $font-family-sans-serif: 'Nunito', sans-serif; $font-size-base: 0.9rem; $line-height-base: 1.6; // Colors $blue: #3490dc; $indigo: #6574cd; $purple: #9561e2; $pink: #f66d9b; $red: #e3342f; $orange: #f6993f; $yellow: #ffed4a; $green: #38c172; $teal: #4dc0b5; $cyan: #6cb2eb; |
Minty versión _variables.scss
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | // Minty 4.3.1 // Bootswatch // // Color system // $white: #fff !default; $gray-100: #f8f9fa !default; $gray-200: #f7f7f9 !default; $gray-300: #eceeef !default; $gray-400: #ced4da !default; $gray-500: #aaa !default; $gray-600: #888 !default; $gray-700: #5a5a5a !default; $gray-800: #343a40 !default; $gray-900: #212529 !default; $black: #000 !default; $blue: #007bff !default; $indigo: #6610f2 !default; $purple: #6f42c1 !default; $pink: #e83e8c !default; $red: #FF7851 !default; $orange: #fd7e14 !default; $yellow: #FFCE67 !default; $green: #56CC9D !default; $teal: #20c997 !default; $cyan: #6CC3D5 !default; $primary: #78C2AD !default; $secondary: #F3969A !default; $success: $green !default; $info: $cyan !default; $warning: $yellow !default; $danger: $red !default; $light: $gray-100 !default; $dark: $gray-800 !default; $yiq-contrasted-threshold: 250 !default; // Body $body-color: $gray-600 !default; // Components $border-radius: .4rem !default; $border-radius-lg: .6rem !default; $border-radius-sm: .3rem !default; // Fonts $headings-font-family: "Montserrat", -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !default; $headings-color: $gray-700 !default; // Tables $table-border-color: rgba(0,0,0,0.05) !default; // Dropdowns $dropdown-link-hover-color: $white !default; $dropdown-link-hover-bg: $secondary !default; // Navbar $navbar-dark-color: rgba($white,.6) !default; $navbar-dark-hover-color: $white !default; $navbar-light-color: rgba($black,.3) !default; $navbar-light-hover-color: $gray-700 !default; $navbar-light-active-color: $gray-700 !default; $navbar-light-disabled-color: rgba($black,.1) !default; // Pagination $pagination-color: $white !default; $pagination-bg: $primary !default; $pagination-border-color: $primary !default; $pagination-hover-color: $white !default; $pagination-hover-bg: $secondary !default; $pagination-hover-border-color: $pagination-hover-bg !default; $pagination-active-bg: $secondary !default; $pagination-active-border-color: $pagination-active-bg !default; $pagination-disabled-color: $white !default; $pagination-disabled-bg: #CCE8E0 !default; $pagination-disabled-border-color: $pagination-disabled-bg !default; // Breadcrumbs $breadcrumb-bg: $primary !default; $breadcrumb-divider-color: $white !default; $breadcrumb-active-color: $breadcrumb-divider-color !default; |
Este archivo establece valores para todas estas variables, lo que le da a Bootstrap un aspecto completamente nuevo. Ahora podemos modificar el archivo welcome.blade.php así solo para una prueba. Queremos probar diferentes clases de Bootstrap para ver si se ha producido el nuevo efecto.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <!doctype html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Laravel</title> <!-- Styles --> <link rel="stylesheet" href="{{ asset('/css/app.css') }}"> </head> <body class="container"> <div class="jumbotron mt-3"> <h1 class="display-3">Hello, world!</h1> <p class="lead">This is a simple hero unit!</p> <hr class="my-4"> <p>It uses utility classes for typography and spacing to space content out within the larger container.</p> <button type="button" class="btn btn-primary">Primary</button> <button type="button" class="btn btn-secondary">Secondary</button> <button type="button" class="btn btn-success">Success</button> <button type="button" class="btn btn-info">Info</button> <button type="button" class="btn btn-warning">Warning</button> <button type="button" class="btn btn-danger">Danger</button> <button type="button" class="btn btn-link">Link</button> </div> <script src="{{ asset('js/app.js') }}"></script> </body> </html> |
Ejecutar Mix
Ahora podemos ejecutar la mezcla con npm run dev
y debería haber un resultado similar a este.
Nota: Si se encuentra con errores al ejecutar mix, consulte esta publicación que debería ayudar.
Ahora, en lugar de la pantalla de presentación estándar, vemos los nuevos estilos aplicados. ¡Frio!
Construyendo un front-end Vue
Con ese poco de instalación y configuración fuera del camino, ahora podemos comenzar a construir el Vue Front end para nuestra API. Solo un par de componentes pueden ser buenos para esto. Tendremos un componente simple del componente Navbar y un componente PostList. Para empezar, simplemente colocaremos las cosas como andamios y luego agregaremos datos dinámicos sobre la marcha.
welcome.blade.php
Laravel todavía va a cargar este archivo de vista como página de inicio. En esa página de inicio, hay un div con la identificación de "aplicación". Nuestra aplicación Vue se adjuntará o se montará a ese div. Aquí están los inicios de ese archivo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <!doctype html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="csrf-token" content="{{ csrf_token() }}"> <script>window.Laravel = {csrfToken: '{{ csrf_token() }}'}</script> <title>Vue Front End For A Laravel API</title> <link rel="stylesheet" href="{{ asset('/css/app.css') }}"> </head> <body class="container-fluid"> <div id="app"> <navbar></navbar> </div> <script src="{{ asset('js/app.js') }}"></script> </body> </html> |
Tenga en cuenta la inclusión de los campos csrf_token. Sin ellos, obtendrá errores de "token CSRF no encontrado: https://laravel.com/docs/csrf#csrf-x-csrf-token" en la consola, así que asegúrese de incluir esas líneas de código.
Agregar un componente de barra de navegación
Notará que en la línea 13 de arriba hay una referencia a un componente <navbar>. Necesitamos construir y registrar ese componente para que se muestre. Podemos navegar al resources/js/components
directorio y crear un archivo Navbar.vue . Este archivo tiene una .vue
extensión especial que significa un par de cosas. La primera es que es un componente de archivo único de Vue. Estos tipos de archivos le permiten colocar la plantilla, el script e incluso el CSS personalizado en el mismo archivo. Luego construye el archivo a través de Webpack (en este caso Laravel Mix) y obtiene un resultado funcional. ¡Muy genial!
recursos / js / components / Navbar.vue
| <template> <nav class="navbar navbar-expand-lg navbar-dark bg-primary"> <a class="navbar-brand" href="#">Minty Fresh</a> </nav> </template> |
Ahora, registramos el componente en resources/js/app.js
.
resources / js / app.js
| require('./bootstrap'); window.Vue = require('vue'); Vue.component('navbar', require('./components/Navbar.vue').default); const app = new Vue({ el: '#app' }); |
Siempre que haya configurado Laravel Mix para ver sus archivos ahora, ya sea ejecutando npm run watch
o npm run watch-poll
, entonces debería poder visitar la página de inicio y ver una barra de navegación Minty.
Listado de publicaciones
Para enumerar algunas publicaciones, podemos agregar un nuevo componente llamado PostList.vue en resources/js/components
. Entonces, primero, continúe y agregue ese archivo como vemos aquí.
El nuevo componente debe registrarse en el archivo app.js.
resources / js / app.js
| require('./bootstrap'); window.Vue = require('vue'); Vue.component('navbar', require('./components/Navbar.vue').default); Vue.component('post-list', require('./components/PostList.vue').default); const app = new Vue({ el: '#app' }); |
Ahora, en el archivo PostList.vue , necesitamos completar las áreas de plantilla y script. Hacemos uso del Lifecycle Hook creado. Esta función se llama automáticamente justo después de que se haya creado la instancia del componente. Esto es justo antes de que el componente se monte en la página, por lo que es el momento perfecto para obtener los datos de la API que necesitaremos usando la API de JavaScript Fetch .
recursos / js / components / PostList.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <template> <div> <div class="card mb-2" v-for="post in posts" v-bind:key="post.id"> <div class="card-body "> <h4 class="card-title">{{ post.title }}</h4> <p class="card-text">{{ post.body }}</p> </div> </div> </div> </template> <script> export default { data() { return { posts: [] }; }, created() { this.getPosts(); }, methods: { getPosts(api_url) { api_url = api_url || '/api/posts'; fetch(api_url) .then(response => response.json()) .then(response => { this.posts = response.data; }) .catch(err => console.log(err)); } } }; </script> |
En el código de arriba, la posts
matriz en la línea 16 es lo que contendrá la respuesta de la API que contiene todas las publicaciones. Se completa cuando se carga la página y se created()
ejecuta el enlace. Cuando eso sucede getPosts()
, se llama a la función, que es la que obtiene los datos de la api y los asigna a la posts
variable. Con los datos en su lugar, la sección de plantillas utiliza su Vue v-for estándar para crear una lista de publicaciones.
Agregar un paginador
Tenemos 5 publicaciones para mostrar con éxito arriba, ¡y se ve muy bien! Recuerde que nuestra API proporciona información sobre los enlaces anteriores, siguientes y actuales. Esto significa que puede usar esos datos para crear un paginador. Las líneas resaltadas a continuación muestran las adiciones para mostrar el paginador.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | <template> <div> <nav> <ul class="pagination justify-content-center"> <li v-bind:class="[{disabled: !pagination.prev_page_url}]" class="page-item"> <a class="page-link" href="#" @click="getPosts(pagination.prev_page_url)">Previous</a> </li> <li class="page-item disabled"> <a class="page-link" href="#">{{ pagination.current_page }} of {{ pagination.last_page }}</a> </li> <li v-bind:class="[{disabled: !pagination.next_page_url}]" class="page-item"> <a class="page-link" href="#" @click="getPosts(pagination.next_page_url)">Next</a> </li> </ul> </nav> <div class="card mb-2" v-for="post in posts" v-bind:key="post.id"> <div class="card-body "> <h4 class="card-title">{{ post.title }}</h4> <p class="card-text">{{ post.body }}</p> </div> </div> </div> </template> <script> export default { data() { return { posts: [], pagination: {} }; }, created() { this.getPosts(); }, methods: { getPosts(api_url) { let vm = this; api_url = api_url || '/api/posts'; fetch(api_url) .then(response => response.json()) .then(response => { this.posts = response.data; vm.paginator(response.meta, response.links); }) .catch(err => console.log(err)); }, paginator(meta, links) { this.pagination = { current_page: meta.current_page, last_page: meta.last_page, next_page_url: links.next, prev_page_url: links.prev }; }, } }; </script> |
Esto da como resultado que se muestre el paginador que nos dice en qué página estamos y habilita o deshabilita dinámicamente los botones anterior y siguiente según el lugar del conjunto en el que nos encontramos.
Agregar una publicación
Agreguemos un formulario a la página para que se pueda agregar una publicación enviando una solicitud POST a la API. En la parte posterior, primero cambiaremos la paginación a 3 por página para darnos más espacio.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | <template> <div> <form @submit.prevent="addPost"> <div class="form-group"> <input type="text" class="form-control" placeholder="Title" v-model="post.title"> </div> <div class="form-group"> <textarea class="form-control" placeholder="Body" v-model="post.body"></textarea> </div> <button type="submit" class="btn btn-success">Save</button> </form> <nav> <ul class="pagination justify-content-center"> <li v-bind:class="[{disabled: !pagination.prev_page_url}]" class="page-item"> <a class="page-link" href="#" @click="getPosts(pagination.prev_page_url)">Previous</a> </li> <li class="page-item disabled"> <a class="page-link" href="#">{{ pagination.current_page }} of {{ pagination.last_page }}</a> </li> <li v-bind:class="[{disabled: !pagination.next_page_url}]" class="page-item"> <a class="page-link" href="#" @click="getPosts(pagination.next_page_url)">Next</a> </li> </ul> </nav> <div class="card mb-2" v-for="post in posts" v-bind:key="post.id"> <div class="card-body "> <h4 class="card-title">{{ post.title }}</h4> <p class="card-text">{{ post.body }}</p> </div> </div> </div> </template> <script> export default { data() { return { posts: [], pagination: {}, post: { id: '', title: '', body: '' } }; }, created() { this.getPosts(); }, methods: { getPosts(api_url) { let vm = this; api_url = api_url || '/api/posts'; fetch(api_url) .then(response => response.json()) .then(response => { this.posts = response.data; vm.paginator(response.meta, response.links); }) .catch(err => console.log(err)); }, paginator(meta, links) { this.pagination = { current_page: meta.current_page, last_page: meta.last_page, next_page_url: links.next, prev_page_url: links.prev }; }, addPost() { fetch('api/post', { method: 'post', body: JSON.stringify(this.post), headers: { 'content-type': 'application/json' } }) .then(response => response.json()) .then(data => { this.getPosts(); }) .catch(err => console.log(err)); } } }; </script> |
Ahora tenemos la posibilidad de agregar una nueva publicación, lo cual es genial.
Eliminar una publicación
Para eliminar una publicación, podemos agregar una nueva función al objeto de métodos llamado deletePost () . Acepta la identificación de la publicación para eliminar, luego realiza una solicitud de eliminación ajax a la API. Los aspectos destacados a continuación muestran el nuevo código para permitir la eliminación de una publicación.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | <template> <div> <form @submit.prevent="addPost"> <div class="form-group"> <input type="text" class="form-control" placeholder="Title" v-model="post.title"> </div> <div class="form-group"> <textarea class="form-control" placeholder="Body" v-model="post.body"></textarea> </div> <button type="submit" class="btn btn-success">Save</button> </form> <nav> <ul class="pagination justify-content-center"> <li v-bind:class="[{disabled: !pagination.prev_page_url}]" class="page-item"> <a class="page-link" href="#" @click="getPosts(pagination.prev_page_url)">Previous</a> </li> <li class="page-item disabled"> <a class="page-link" href="#">{{ pagination.current_page }} of {{ pagination.last_page }}</a> </li> <li v-bind:class="[{disabled: !pagination.next_page_url}]" class="page-item"> <a class="page-link" href="#" @click="getPosts(pagination.next_page_url)">Next</a> </li> </ul> </nav> <div class="card mb-2" v-for="post in posts" v-bind:key="post.id"> <div class="card-body "> <h4 class="card-title">{{ post.title }}</h4> <p class="card-text">{{ post.body }}</p> <button type="button" @click="deletePost(post.id)" class="btn btn-secondary">Delete</button> </div> </div> </div> </template> <script> export default { data() { return { posts: [], pagination: {}, post: { id: '', title: '', body: '' } }; }, created() { this.getPosts(); }, methods: { getPosts(api_url) { let vm = this; api_url = api_url || '/api/posts'; fetch(api_url) .then(response => response.json()) .then(response => { this.posts = response.data; vm.paginator(response.meta, response.links); }) .catch(err => console.log(err)); }, paginator(meta, links) { this.pagination = { current_page: meta.current_page, last_page: meta.last_page, next_page_url: links.next, prev_page_url: links.prev }; }, addPost() { fetch('api/post', { method: 'post', body: JSON.stringify(this.post), headers: { 'content-type': 'application/json' } }) .then(response => response.json()) .then(data => { this.getPosts(); }) .catch(err => console.log(err)); }, deletePost(id) { fetch('api/post/' + id, { method: 'delete' }) .then(response => response.json()) .then(data => { this.getPosts(); }) .catch(err => console.log(err)); }, } }; </script> |
Cuando se elimina una publicación, la página se actualiza automáticamente.
Actualizar una publicación
Podemos agregar un nuevo botón a cada publicación que dará la opción de actualizar esa publicación. Este será una especie de proceso de dos pasos. El primer paso es hacer clic en el botón de actualización para cargar los datos de la publicación en el formulario. Luego, podemos realizar cambios en los datos del formulario y, finalmente, hacer clic en guardar. Eso significa que la función addPost () necesitará una nueva lógica para dar cuenta de eso. Sin embargo, primero veamos cómo agregar el botón para permitir la actualización cargando los datos correctos en el formulario.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | <template> <div> <form @submit.prevent="addPost"> <div class="form-group"> <input type="text" class="form-control" placeholder="Title" v-model="post.title"> </div> <div class="form-group"> <textarea class="form-control" placeholder="Body" v-model="post.body"></textarea> </div> <button type="submit" class="btn btn-success">Save</button> </form> <nav> <ul class="pagination justify-content-center"> <li v-bind:class="[{disabled: !pagination.prev_page_url}]" class="page-item"> <a class="page-link" href="#" @click="getPosts(pagination.prev_page_url)">Previous</a> </li> <li class="page-item disabled"> <a class="page-link" href="#">{{ pagination.current_page }} of {{ pagination.last_page }}</a> </li> <li v-bind:class="[{disabled: !pagination.next_page_url}]" class="page-item"> <a class="page-link" href="#" @click="getPosts(pagination.next_page_url)">Next</a> </li> </ul> </nav> <div class="card mb-2" v-for="post in posts" v-bind:key="post.id"> <div class="card-body "> <h4 class="card-title">{{ post.title }}</h4> <p class="card-text">{{ post.body }}</p> <button type="button" @click="deletePost(post.id)" class="btn btn-secondary">Delete</button> <button type="button" @click="updatePost(post)" class="btn btn-success">Update</button> </div> </div> </div> </template> <script> export default { data() { return { posts: [], pagination: {}, post: { id: '', title: '', body: '' }, update: false, post_id: '' }; }, created() { this.getPosts(); }, methods: { getPosts(api_url) { let vm = this; api_url = api_url || '/api/posts'; fetch(api_url) .then(response => response.json()) .then(response => { this.posts = response.data; vm.paginator(response.meta, response.links); }) .catch(err => console.log(err)); }, paginator(meta, links) { this.pagination = { current_page: meta.current_page, last_page: meta.last_page, next_page_url: links.next, prev_page_url: links.prev }; }, addPost() { fetch('api/post', { method: 'post', body: JSON.stringify(this.post), headers: { 'content-type': 'application/json' } }) .then(response => response.json()) .then(data => { this.getPosts(); }) .catch(err => console.log(err)); }, deletePost(id) { fetch('api/post/' + id, { method: 'delete' }) .then(response => response.json()) .then(data => { this.getPosts(); }) .catch(err => console.log(err)); }, updatePost(post) { this.update = true; this.post.id = post.id; this.post.post_id = post.id; this.post.title = post.title; this.post.body = post.body; } } }; </script> |
Dependiendo de la publicación para la que haga clic en el botón de actualización, esos datos se cargan en el formulario para que podamos tomar medidas al respecto. Entonces, si desea actualizar la publicación 2, puede hacer clic en ese botón de actualización en particular, cambiar los datos y luego guardar.
Modificar la función addPost ()
En la sección anterior, al hacer clic en el botón de actualización de una publicación determinada, se cargan los datos de esa publicación en el formulario. Sin embargo, al hacer clic en Guardar para el formulario, se crearía una nueva publicación, no se actualizaría la que se acaba de cargar en el formulario. Podemos usar alguna lógica condicional en la función addPost () para solucionar este problema. También podemos agregar una función clearForm () para dos propósitos. La primera es permitir que el usuario borre el formulario si decide no hacer una actualización y, en segundo lugar, borrar automáticamente el formulario una vez que se agrega una nueva publicación.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | <template> <div> <form @submit.prevent="addPost"> <div class="form-group"> <input type="text" class="form-control" placeholder="Title" v-model="post.title"> </div> <div class="form-group"> <textarea class="form-control" placeholder="Body" v-model="post.body"></textarea> </div> <button type="submit" class="btn btn-success">Save</button> <button @click.prevent="clearForm()" class="btn btn-warning">Clear Form</button> </form> <nav> <ul class="pagination justify-content-center"> <li v-bind:class="[{disabled: !pagination.prev_page_url}]" class="page-item"> <a class="page-link" href="#" @click="getPosts(pagination.prev_page_url)">Previous</a> </li> <li class="page-item disabled"> <a class="page-link" href="#">{{ pagination.current_page }} of {{ pagination.last_page }}</a> </li> <li v-bind:class="[{disabled: !pagination.next_page_url}]" class="page-item"> <a class="page-link" href="#" @click="getPosts(pagination.next_page_url)">Next</a> </li> </ul> </nav> <div class="card mb-2" v-for="post in posts" v-bind:key="post.id"> <div class="card-body "> <h4 class="card-title">{{ post.title }}</h4> <p class="card-text">{{ post.body }}</p> <button type="button" @click="deletePost(post.id)" class="btn btn-secondary">Delete</button> <button type="button" @click="updatePost(post)" class="btn btn-success">Update</button> </div> </div> </div> </template> <script> export default { data() { return { posts: [], pagination: {}, post: { id: '', title: '', body: '' }, update: false, post_id: '' }; }, created() { this.getPosts(); }, methods: { getPosts(api_url) { let vm = this; api_url = api_url || '/api/posts'; fetch(api_url) .then(response => response.json()) .then(response => { this.posts = response.data; vm.paginator(response.meta, response.links); }) .catch(err => console.log(err)); }, paginator(meta, links) { this.pagination = { current_page: meta.current_page, last_page: meta.last_page, next_page_url: links.next, prev_page_url: links.prev }; }, addPost() { if (this.update === false) { fetch('api/post', { method: 'post', body: JSON.stringify(this.post), headers: { 'content-type': 'application/json' } }) .then(response => response.json()) .then(data => { this.clearForm(); this.getPosts(); }) .catch(err => console.log(err)); } else { fetch('api/post', { method: 'put', body: JSON.stringify(this.post), headers: { 'content-type': 'application/json' } }) .then(response => response.json()) .then(data => { this.clearForm(); this.getPosts(); }) .catch(err => console.log(err)); } }, deletePost(id) { fetch('api/post/' + id, { method: 'delete' }) .then(response => response.json()) .then(data => { this.getPosts(); }) .catch(err => console.log(err)); }, updatePost(post) { this.update = true; this.post.id = post.id; this.post.post_id = post.id; this.post.title = post.title; this.post.body = post.body; }, clearForm() { this.update = false; this.post.id = null; this.post.post_id = null; this.post.title = ''; this.post.body = ''; } } }; </script> |
Entonces, para el resultado final, podemos crear, leer, actualizar y eliminar por completo las publicaciones usando Vue en la parte frontal y Laravel en la parte posterior.
Creación de un front-end Vue para un resumen de la API de Laravel
Con eso, ahora tenemos una interfaz de Vue para el recurso de API de Laravel que habíamos construido en el tutorial anterior. Construimos esto dentro de Laravel usando la herramienta Mix para habilitar un proceso de construcción que compilaba los archivos .vue en JavaScript listo para producción.
0 Comentarios