Laravel: Filtered List with Blade & Vue

Build a reactive filtered list with Blade & Vue

Prepare your blade template

@bot    // static contant goes here...@else    <div id="filter"></div>@endbot

The Vue-App

Your app consists of two files, the entry-point post-filter/index.js:

import Vue from 'vue';import VueRouter from 'vue-router';Vue.use(VueRouter);const App = { template: '<div id="filter"><router-view /></div>' };const router = new VueRouter({    mode: 'history',    routes: [        {            path: '/filter/:categories?',            name: 'filter',            component: () => import('./Filter'),            props(route) {                const categories = route.params.categories?.toString() || null;                return {                    categories: categories ? categories.split(',') : [],                };            },        },    ],});const app = new Vue({    render: (h) => h(App),    router,}).$mount('#filter');

and the post-filter/Filter.vue:

<template>    <div>        <label>            <input type="checkbox" value="1" v-model="categories" />            Category 1        </label>        <label>            <input type="checkbox" value="2" v-model="categories" />            Category 2        </label>        <transition-group            name="list-complete"            class="flex flex-row flex-wrap w-full list-complete"            tag="section"        >            <div                v-for="post in posts"                :key="post.id"                class="w-1/3 p-5 transition-transform duration-200"            >                <h3 class="text-xl">{{ post.title }}</h3>                {{ post.body }}            </div>        </transition-group>    </div></template><script>export default {    name: 'PostFilter',    props: {        /**         * We get an array with categories from the router.         * It is a representation of the categories from the route param.         */        categories: {            type: Array,        },    },    data() {        return {            posts: [],        };    },    /**     * The mounted hook will only get called once.     * A route change of the vue router will not affect this hook.     */    mounted() {        this.getPosts();    },    watch: {        /**         * Whenever the list of categories changes, we need to redirect         * with the new route params.         */        sortedCategories(val) {            // make a string from the sorted array [1,2] => '1,2'            // or set to null if empty            let categories = this.sortedCategories.join(',') || null;            // check if the params differ to prevent double route hits            if (this.$route.params.categories != categories) {                // go to filter route with new params                this.$router.push({                    name: 'filter',                    params: {                        categories,                    },                });                // fetch a filtered set of posts                this.getPosts();            }        },    },    methods: {        async getPosts() {            this.posts = await axios.post('/api/enpoint', {                query: this.sortedCategories,            });        },    },    computed: {        sortedCategories() {            return this.categories.sort();        },    },};</script><style scoped>.list-complete-enter,.list-complete-leave-to {    opacity: 0;    transform: translateY(0px);}.list-complete-leave-active {    position: absolute;}</style>