CRUD in Vue + Rest Api CodeIgniter 4 - 48

Video thumbnail

Now that the CRUD process is defined for posts, let's follow the same process for categories, creating their components in Vue. It looks like this:

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>

Grouping routes

Since we can use the Vue application for different modules, for example, we could create another module for the end user in the same application where we created the dashboard, it is very useful to group the routes; to do this, we can use the following configuration:

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
            }
        ]
    }
]

As you can see, we replaced the base routes and made them children of the children option, in which we defined the route prefix that must always start with a /:

path: '/dashboard',

Followed by the child routes that were given a better name; remember to update references to names throughout the application, for example:

src/components/CRUD/Movies/ListComponent.vue

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

Specific component

We can also create a Base component for these routes, i.e. not just use the one from App.vue; which is particularly useful to be able to make a clear distinction between different modules of our application using a different design; to do this:

src/components/CRUD/Base.vue

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

And since we have demonstratively defined the container in the new component, we remove it from App.vue:

src/App.vue

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

In this way, you can now create other fully customizable modules by going through their design.

I agree to receive announcements of interest about this Blog.

You will create the CRUD for categories for the Rest API in CodeIgniter 4.

- Andrés Cruz

En español