لارافيل, VueJs / 2024-05-14

سلسة عمليات CRUD بإستخدام Laravel + VueJs مع SPA - الجزء الثاني عشر - كيفية عمل LiveSearch بإستخدام Laravel + vuejs

سلسة عمليات CRUD بإستخدام Laravel + VueJs مع SPA - الجزء الثاني عشر - كيفية عمل LiveSearch بإستخدام Laravel + vuejs

2024-05-14 وقت القراءه : 6 دقائق

في أي لوحة تحكم أو موقع لا بد من توفر خاصية البحث، ومن أجل تجربة مستخدم أفضل يفضل أن يكون البحث بشكل مباشر عند كتابة أي حرف في خانة البحث ودون الضغط على زر بحث.

في هذا المقال وضمن سلسة تعلم VueJs + Laravel سيتم شرح ذلك.


تحضير Front End  

في الملف resources/js/components/Posts/index.vue نضيف textField مخصص للبحث

<div class="col-6">
    <input v-model="title_search" type="text" class="form-control" placeholder="Search For Title">
</div>

فيصبح لدينا الشكل التالي

توضيح

  • سأطبق البحث فقط على title.
  • تم إستخدام v-model=title_search، ذلك يعني أنه يجب ان يكون لدي متغير في دالة data بنفس الإسم.
  • يجب إستخدام الدالة watch لمراقبة اي تغيير على الحقل، بالطبع يجب إستخدام نفس الإسم.
data() {
    return {
        posts: {},
        categories: {},
        category_id: '',
        sort_field: 'created_at',
        sort_direction: 'desc',
        title_search:'',
    }
},


watch: {
    category_id(value) {
        this.getResults()
    },
    title_search(){
        this.getResults();
    }
},

كما نرى أن الدالة title_search ستراقب أي تغير في textField وعند حدوث أي تغيير فإنها سوف تستدعي الدالة this.getResults الموجوده في methods

getResults(page = 1) {
    axios.get('/api/posts?page=' + page
        + '&category_id=' + this.category_id
        + '&sort_field=' + this.sort_field
        + '&sort_direction=' + this.sort_direction
        + '&title_search=' + this.title_search)
        .then(response => {
            this.posts = response.data;
        });
},


كما نلاحظ في الدالة getResults فإنه سيتم إرسال متغير جديد بإسم title_search وهو ما سيتم إستقباله في لارافيل.


تحضير back End

في الدالة index سيتم إستخدام when

public function index()
{
    $sortField=\request('sort_field', 'created_at');
    if(!in_array($sortField,['id','title','body','created_at'])){
        $sortField = 'created_at';
    }
    $sortDirection=\request('sort_direction', 'desc');
    if(!in_array($sortDirection,['asc','desc'])){
        $sortDirection = 'desc';
    }
    $posts=Post::when(request('category_id','') !='',function ($q){
        $q->where('category_id',request('category_id'));
    })->when(request('title_search','') !='',function ($q){
        $q->where('title','LIKE','%'.request('title_search').'%');
    })
        ->orderBy($sortField,$sortDirection)
        ->paginate(10);
    return PostResource::collection($posts);
}


صفحة الكود بالكامل

<template>
    <div class="container">
        <br/>
        <div class="row">
            <div class="col-6">
                <select v-model="category_id" class="form-control">
                    <option value="">--Choose Category--</option>
                    <option v-for="category in categories" :value="category.id">
                        {{ category.name }}
                    </option>
                </select>
            </div>
            <div class="col-6">
                <input v-model="title_search" type="text" class="form-control" placeholder="Search For Title">
            </div>
        </div>
        <br/>
        <table class="table">
            <thead>
            <tr>
                <th scope="col">
                    <a href="#" @click.prevent="change_sort('id')">id</a>
                    <span
                        :class="this.sort_field === 'id' && this.sort_direction === 'asc' ? 'text-dark' : 'text-black-50'">&uarr;</span>
                    <span
                        :class="this.sort_field === 'id' && this.sort_direction === 'desc' ? 'text-dark' : 'text-black-50'">&darr;</span>
                </th>
                <th scope="col">
                    <a href="#" @click.prevent="change_sort('title')">title</a>
                    <span
                        :class="this.sort_field === 'title' && this.sort_direction === 'asc' ? 'text-dark' : 'text-black-50'">&uarr;</span>
                    <span
                        :class="this.sort_field === 'title' && this.sort_direction === 'desc' ? 'text-dark' : 'text-black-50'">&darr;</span>
                </th>
                <th scope="col">
                    <a href="#" @click.prevent="change_sort('body')">body</a>
                    <span
                        :class="this.sort_field === 'body' && this.sort_direction === 'asc' ? 'text-dark' : 'text-black-50'">&uarr;</span>
                    <span
                        :class="this.sort_field === 'body' && this.sort_direction === 'desc' ? 'text-dark' : 'text-black-50'">&darr;</span>
                </th>
                <th scope="col">
                    <a href="#" @click.prevent="change_sort('created_at')">created_at</a>
                    <span
                        :class="this.sort_field === 'created_at' && this.sort_direction === 'asc' ? 'text-dark' : 'text-black-50'">&uarr;</span>
                    <span
                        :class="this.sort_field === 'created_at' && this.sort_direction === 'desc' ? 'text-dark' : 'text-black-50'">&darr;</span>
                </th>
                <th>Edit</th>
                <th>Delete</th>
            </tr>
            </thead>
            <tbody>
            <tr v-for="post in posts.data" :key="post.id">
                <td>{{ post.id }}</td>
                <td>{{ post.title }}</td>
                <td>{{ post.body.substring(0, 30) }}</td>
                <td>{{ post.created_at }}</td>
                <td>
                    <router-link :to="{ name:'posts.edit', params:{ id:post.id} }" class="btn btn-success">Edit
                    </router-link>
                </td>
                <td>
                    <button @click="delete_post(post.id)" class="btn btn-danger">Delete</button>
                </td>
            </tr>
            </tbody>
        </table>
        <pagination :data="posts" @pagination-change-page="getResults"></pagination>
    </div>
</template>


<script>
export default {
    data() {
        return {
            posts: {},
            categories: {},
            category_id: '',
            sort_field: 'created_at',
            sort_direction: 'desc',
            title_search:'',
        }
    },
    mounted() {
        axios.get('/api/category')
            .then(response => {
                this.categories = response.data.data;
            });
        this.getResults();
    },
    watch: {
        category_id(value) {
            this.getResults()
        },
        title_search(){
            this.getResults();
        }
    },
    methods: {
        change_sort(field) {
            if (this.sort_field === field) {
                this.sort_direction = this.sort_direction === 'asc' ? 'desc' : 'asc';
            } else {
                this.sort_field = field;
                this.sort_direction = 'asc';
            }
            this.getResults();
        },
        getResults(page = 1) {
            axios.get('/api/posts?page=' + page
                + '&category_id=' + this.category_id
                + '&sort_field=' + this.sort_field
                + '&sort_direction=' + this.sort_direction
                + '&title_search=' + this.title_search)
                .then(response => {
                    this.posts = response.data;
                });
        },
        delete_post(id) {
            swal({
                title: "Are Your Shure You Want to Delete The Post ?",
                icon: "warning",
                buttons: ["No", "Yes"],
                dangerMode: true,
            })
                .then((willDelete) => {
                    if (willDelete) {
                        axios.delete('/api/posts/' + id).then(response => {
                            swal("Post Deleted Successfully");
                            this.getResults()
                        })
                    } else {
                        swal("Post Not Deleted");
                    }
                }).catch(error => {
                swal({
                    icon: 'error',
                    title: 'Error Happened'
                });
            });
        }
    }
}
</script>


إضافة تعليق
Loading...