CRUD en Vue + Rest Api CodeIgniter 4 - 48

Video thumbnail

Ya con el proceso CRUD definido para los post. Vamos a hacer el mismo proceso para el de categorías, creando sus componentes en Vue es decir, quedando como:

src/components/CRUD/Categories/ListComponent.vue

<template>
    <o-modal v-model:active="confirmDeleteActive">
        <div class="m-4">
            <p class="mb-3">Are you sure you want to delete the <span class="font-bold">{{ deleteCategoryRow && deleteCategoryRow.row ? deleteCategoryRow.row.titulo : '' }}</span> record?</p>
            <div class="flex gap-2 flex-row-reverse">
                <o-button @click="confirmDeleteActive = false">Cancel</o-button>
                <o-button variant="danger" @click="remove">Delete</o-button>
            </div>
        </div>
    </o-modal>
    <h1>List</h1>
    <div class="card">
        <div class="mb-4 ms-2">
            <o-button @click="$router.push({ name: 'category.save' })" variant="primary" size="large">Create</o-button>
        </div>
        <o-table :data="categories" :bordered="true" :striped="true" :hoverable="true" :selectable="true">
            <o-table-column field="id" label="ID" v-slot="c" sortable>
                {{ c.row.id }}
            </o-table-column>
            <o-table-column field="titulo" label="Title" v-slot="c" sortable>
                {{ c.row.titulo }}
            </o-table-column>
            <o-table-column label="Actions" v-slot="c">
                <o-button @click="$router.push({ name: 'category.save', params: { id: c.row.id } })"
                    icon-left="pencil">Edit</o-button>
                <div class="inline ms-3">
                    <o-button variant="danger" size="small" @click="confirmDeleteActive = true; deleteCategoryRow = m"
                        icon-left="delete">Delete</o-button>
                </div>
            </o-table-column>
        </o-table>
    </div>
</template>
<script>
export default {
    created() {
        this.getCategories();
    },
    data() {
        return {
            categories: [],
            confirmDeleteActive: false,
            deleteCategoryRow: ''
        }
    },
    methods: {
        remove() {
            axios.delete('http://code4movies.test/api/categoria/' + this.deleteCategoryRow.row.id)
                .then(res => res.data)
                .catch(error => error)
            this.categories.splice(this.deleteCategoryRow.index, 1)
            this.confirmDeleteActive = false
        },
        async getCategories() {
            this.categories = await axios.get('http://code4movies.test/api/categoria')
                .then(res => res.data)
                .catch(error => error)
        },
    },
}
</script>

src/components/CRUD/Categories/SaveComponent.vue

<template>
    <h1>
        <template v-if="category">
            Update: <span class="font-bold"> {{ category.titulo }}</span>
        </template>
        <template v-else>
            Create
        </template>
    </h1>
    <div class="card max-w-lg mx-auto">
        <!-- <o-field label="Title"> -->
        <o-field label="Title" :variant="errors.title ? 'danger' : ''" :message="errors.title">
            <o-input v-model="form.title" expanded></o-input>
        </o-field>
        <o-button variant="primary" @click="send">Send</o-button>
    </div>
</template>
<script>
export default {
    data() {
        return {
            category: '',
            form: {
                title: '',
            },
            errors: {
                title: '',
            }
        }
    },
    async mounted() {
        if (this.$route.params.id) {
            await this.getCategory()
            this.init()
        }
    },
    methods: {
        init() {
            this.form.title = this.category.titulo
        },
        async getCategory() {
            this.category = await axios.get('http://code4movies.test/api/categoria/' + this.$route.params.id)
                .then(res => res.data)
                .catch(error => error)
        },
        async send() {
            let res = ''
            if (this.category == '') {
                const formData = new FormData()
                formData.append('titulo', this.form.title)
                res = await axios.post('http://code4movies.test/api/categoria', formData)
                    .then(res => res.data) // 200
                    .catch(error => error) // 500-400
                this.cleanForm()
            } else {
                res = await axios.put('http://code4movies.test/api/categoria/' + this.category.id, this.form)
                    .then(res => res.data) // 200
                    .catch(error => error) // 500-400
                this.cleanForm()
            }
            // error
            if (res.response && res.response.data.titulo) {
                this.errors.title = res.response.data.titulo
            }
        },
        cleanForm() {
            this.errors = {
                title: ''
            }
        }
    },
}
</script>

Agrupar rutas

Al poder emplear la aplicación en Vue para distintos módulos, por ejemplo, pudiéramos crear otro módulo para el usuario final en la misma aplicación que hemos creado el dashboard, resulta muy útil agrupar las rutas; para ello, podemos emplear la siguiente configuración:

const routes = [
    {
        path: '/dashboard',
        component: Base,
        children: [
            {
                name: 'movie.list',
                path: 'movie',
                component: ListMovie
            },
            {
                name: 'movie.save',
                path: 'movie/save',
                component: SaveMovie
            },
            {
                name: 'movie.save',
                path: 'movie/save/:id?',
                component: SaveMovie
            },
            {
                name: 'category.list',
                path: 'category',
                component: ListCategory
            },
            {
                name: 'category.save',
                path: 'category/save',
                component: SaveCategory
            },
            {
                name: 'category.save',
                path: 'category/save/:id?',
                component: SaveCategory
            }
        ]
    }
]

Como puedes apreciar, reemplazamos las rutas bases y las hicimos hijas de la opción children, en la cual, definimos el prefijo de las rutas que siempre debe de comenzar con un /:

path: '/dashboard',

Seguido de las rutas hijas que se les dio un mejor nombre; recuerda con esto, actualizar las referencias a los nombres en toda la aplicación, por ejemplo:

src/components/CRUD/Movies/ListComponent.vue

<o-button @click="$router.push({ name: 'movie.save' })" variant="primary" size="large">Create</o-button>

Componente específico

También, podemos crear un componente Base para estas rutas, es decir, no solamente emplear el de App.vue; lo cual es particularmente útil para poder hacer una clara distinción entre distintos módulos de nuestra aplicación utilizando un diseño distinto; para ello:

src/components/CRUD/Base.vue

<template>
 <div class="container mx-auto my-3">
    <router-view></router-view>
  </div>
</template>

Y ya que definimos de manera demostrativa el container en el nuevo componente, lo quitamos del App.vue:

src/App.vue

<template>
    <router-view></router-view>
</template>

De esta forma, ahora puedes crear otros módulos completamente personalizables pasando por su diseño.

Acepto recibir anuncios de interes sobre este Blog.

Vas a crear el CRUD para las categorías para la Rest API en CodeIgniter 4.

- Andrés Cruz

In english