Start learning today

Choose Your Plan

Build real life apps. Learn by creating.

Pay Monthly

12

Pay Yearly

10

Build nested commenting system using Laravel and VueJs - Part 3

Commenting on posts

Now that we have our feed being shown and we can post new status, let's start working on comments.

Go to App/Http/Controllers/Api/CommentController and edit your store function which will add a new comment to a post.

public function store(Request $request)
    {
        try {
            $post = Post::where('id', '=', Input::get('post_id'))->first();
            $post->addComment([
                'content' => $request->get('content'),
                'parent_id' => $request->get('parent_id', null)
                ]);
            return response()->json($this->successResponse($post->getThreadedComments(),''));
        } catch (\Exception $exception) {
            return response()->json($exception->getMessage());
        }
    }

For displaying recursive comments, will make use of recursive components in vuejs which will call one another depending on the nested level. When a comment has replies it will display those replies and go back to the root.

Inside resources/assets/js/components create a new component called CommentList.vue

<template>
    <ul class="list-group">
        <comment v-for="comment in comments" v-bind:comment="comment" v-bind:collection="collection"></comment>
    </ul>
</template>
<script>
    export default{
        props: ['collection','comments'],
        computed:{
        },
        beforeCreate: function () {
            this.$options.components.Comment = require('./Comment.vue')
        }
    }
</script>

Since, this component will make use of one more component called Comment.vue let's create it inside the same directory.

<template>
    <li class="list-group-item">
        {{ comment.user.name }} said {{ comment.content }}
        <a @click="replyToComment = comment">Reply</a>
        <comment-form v-if="replyToComment == comment" :comment="comment"></comment-form>
        <comment-list v-if="collection[comment.id]" v-bind:comments="collection[comment.id]" v-bind:collection="collection"></comment-list>
    </li>
</template>
<script>
    import CommentList from './CommentList.vue';
    import CommentForm from './CommentForm.vue';
    export default{
        props: ['comment', 'collection'],
        data(){
            return {
                replyToComment: false
            }
        },
        mounted(){
        },
        components: {
            'comment-list': CommentList,
            'comment-form': CommentForm
        }
    }
</script>

When user clicks on reply, we need to show a reply form with one input box, let's create CommentForm.vue in the same direcoty

<template>
    <div>
        <input type="text" v-model="content" class="form-control" v-on:keyup.enter="replyTo(comment)">
    </div>
</template>
<style>
</style>
<script>
    export default{
        props: ['comment'],
        data(){
            return{
                content: ''
            }
        },
        methods:{
            replyTo(comment){
                axios.post('/api/comment', {content: this.content, post_id: comment.post_id, parent_id: comment.id}).then(response => {
                   this.content = '';
                   if (!response.data.error) {
                    this.content = '';
                    let payLoad = {
                        post_id: comment.post_id,
                        comments: response.data.data
                    };
                    this.$store.commit('updateComments',payLoad);
                }
            });
            }
        }
    }
</script>

And one last step we need to edit our Feed.vue to display comments

<template>
    <div>
        <div class="panel panel-default" v-for="post in posts">
            <div class="panel-heading">
                {{ post.user.name }} updated status
            </div>
            <div class="panel-body">
                <h4>
                    {{ post.content }}
                </h4>
            </div>
            <div class="panel-footer">
                <div class="col-md-12">
                    <input type="text" class="form-control" v-model="post.reply" placeholder="leave a comment..." 
                    v-on:keyup.enter="comment(post)">
                </div>
                <div class="col-md-12">
                    <comment-list v-if="post.comments" :collection="post.comments" :comments="post.comments.root"></comment-list>
                </div>
                <div class="clearfix"></div>
            </div>
        </div>
    </div>
</template>
<script>
    import CommentList from './CommentList.vue';
    export default {
        data(){
            return {
                post: {
                    reply: ''
                }
            }
        },
        computed: {
            posts(){
                return this.$store.state.posts;
            }
        },
        components: {
            'comment-list': CommentList
        },
        mounted(){
            this.getPosts();
        },
        methods: {
            getPosts(){
                axios.get('/api/post').then(response => {
                    if(!response.data.error){
                        response.data.data.forEach((post) => {
                            this.$store.commit('pushPost',post);
                        });
                    }
                });
            },
            comment(post){
                axios.post('/api/comment', {content: post.reply, post_id: post.id}).then(response => {
                    if (!response.data.error) {
                        post.reply = '';
                        let payLoad = {
                            post_id: post.id,
                            comments: response.data.data
                        };
                        this.$store.commit('updateComments',payLoad);
                    }
                });
            }
        }
    }
</script>

and we are done, now npm run dev and you should see that each post will have a comment box.