Menu

37. Vue 回复添加组件

本节说明

  • 对应视频教程第 37 小节:A New Reply Vue Component

本节内容

本节我们将添加回复的区域抽取成 Vue 组件。首先新建组件:
forum\resources\assets\js\components\NewReply.vue

<template>
    <div>
            <div class="form-group">
                <textarea name="body"
                          id="body"
                          class="form-control"
                          placeholder="说点什么吧..."
                          rows="5"
                          required
                          v-model="body"></textarea>
            </div>

            <button type="submit" class="btn btn-default" @click="addReply">
                提交
            </button>

        <!--<p class="text-center">请先<a href="{{ route('login') }}">登录</a>,然后再发表回复 </p>-->

    </div>
</template>

<script>
    export default {
        data() {
            return {
                body:'',
                endpoint:'/threads/atque/5/replies'
            };
        },

        methods: {
            addReply() {
                axios.post(this.endpoint, { body:this.body })
                    .then(({data}) => {
                       this.body = '';

                       flash('Your reply has been posted.');

                       this.$emit('created',data);
                    });
            }
        }
    }
</script>

注意:以上的post提交路径为endpoint:'/threads/atque/5/replies,为了方便快速完成功能而暂时写死,稍后进行完善。你的路径与以上所示路径会有不同。

注释掉视图相应区域:
forum\resources\views\threads\show.blade.php

    .
    .
    <replies :data="{{ $thread->replies }}" @removed="repliesCount--"></replies>

    {{--@foreach ($replies as $reply)--}}
        {{--@include('threads.reply')--}}
    {{--@endforeach--}}

    {{--{{ $replies->links() }}--}}

    {{--@if (auth()->check())--}}
        {{--<form method="post" action="{{ $thread->path() . '/replies' }}">--}}

            {{--{{ csrf_field() }}--}}

            {{--<div class="form-group">--}}
                {{--<textarea name="body" id="body" class="form-control" placeholder="说点什么吧..."rows="5"></textarea>--}}
            {{--</div>--}}

            {{--<button type="submit" class="btn btn-default">提交</button>--}}
        {{--</form>--}}
    {{--@else--}}
        {{--<p class="text-center">请先<a href="{{ route('login') }}">登录</a>,然后再发表回复 </p>--}}
    {{--@endif--}}
    </div>
    .
    .

接下来在Replies.vue组件中引入:
forum\resources\assets\js\components\Replies.vue

<template>
    <div>
        <div v-for="(reply ,index) in items">
            <reply :data="reply" @deleted="remove(index)"></reply>
        </div>

        <new-reply @created="add"></new-reply>
    </div>
</template>

<script>
    import Reply from './Reply';
    import NewReply from './NewReply';

    export default {
        props: ['data'],

        components: { Reply,NewReply },

        data() {
            return {
                items:this.data
            }
        },

        methods: {
            add(reply){
                this.items.push(reply);
            },

            remove(index) {
                this.items.splice(index,1);

                this.$emit('removed');

                flash('Reply has been deleted!');
            }
        }
    }
</script>

我们点击NewReply.vue组件的提交按钮,触发addReply函数,然后我们提交回复,并触发createdReplies.vue父组件监听到created事件,触发add函数,并将新的回复添加到回复区域,重新渲染回复区域显示所有回复。但是要注意的是,我们发送的是Ajax请求,所以我们要修改一下控制器:
forum\app\Http\Controllers\RepliesController.php

    .
    .
    public function store($channelId,Thread $thread)
    {
        $this->validate(request(),['body' => 'required']);

        $reply = $thread->addReply([
            'body' => request('body'),
            'user_id' => auth()->id(),
        ]);

        if(request()->expectsJson()){
            return $reply->load('owner');
        }

        return back()->with('flash','Your reply has been left.');
    }
    .
    .

注意:我们预加载了owner$reply->load('owner'),因为我们的回复显示组件会使用到。

最后,我们修改一下addReply方法:
forum\app\Thread.php

    .
    .
    public function addReply($reply)
    {
        return $this->replies()->create($reply);
    }
    .
    .

我们来测试一下:
file
注意一点,我们返回了owner,但是看一下owner有哪些信息:
file
我们不想返回email,进行处理:
forum\app\User.php

    .
    .
    protected $hidden = [
        'password', 'remember_token','email'
    ];
    .
    .

file
接下来我们处理无权限用户提交回复的问题。现在我们的页面,未登录用户也能看到提交回复区域,我们需要进行修复:
forum\resources\assets\js\components\NewReply.vue

<template>
    <div>
        <div v-if="signedIn">
            <div class="form-group">
                <textarea name="body"
                          id="body"
                          class="form-control"
                          placeholder="说点什么吧..."
                          rows="5"
                          required
                          v-model="body"></textarea>
            </div>

            <button type="submit" class="btn btn-default" @click="addReply">
                提交
            </button>
        </div>

        <p class="text-center" v-else>
            请先<a href="/login">登录</a>,然后再发表回复
        </p>

    </div>
</template>

<script>
    export default {
        data() {
            return {
                body:'',
                endpoint:'/threads/atque/5/replies'
            };
        },

        computed:{
            signedIn(){
                return window.App.signedIn;
            }
        },

        methods: {
            addReply() {
                axios.post(this.endpoint, { body:this.body })
                    .then(({data}) => {
                       this.body = '';

                       flash('Your reply has been posted.');

                       this.$emit('created',data);
                    });
            }
        }
    }
</script>

如果用户signedIn,才显示回复区域:
file
不要忘记,在之前为了方便,我们的uri是写死的,我们要进行修复:
forum\resources\assets\js\components\Replies.vue

<template>
    <div>
        .
        .
        <new-reply :endpoint="endpoint" @created="add"></new-reply>
    </div>
</template>
<script>
       .
       .
        data() {
            return {
                items:this.data,
                endpoint: location.pathname+'/replies'
            }
        },
        .
        .
</script>

forum\resources\assets\js\components\NewReply.vue

.
.
<script>
    export default {
        props :['endpoint'],

        data() {
            return {
                body:''
            };
        },
        .
        .
</script>

我们利用location.pathname+'/replies'拼接成我们需要的uri,并且在NewReply.vue组件的props属性中获取到uri。我们还剩下最后一件事情要做,那就是我们新增回复,回复数也应该加 1 。这很好解决,仿照我们删除回复即可:
forum\resources\views\threads\show.blade.php

.
.
<replies :data="{{ $thread->replies }}"
     @added="repliesCount++"
     @removed="repliesCount--"></replies>
 .
 .

forum\resources\assets\js\components\Replies.vue

<script>
    .
    .
    methods: {
        add(reply){
            this.items.push(reply);

            this.$emit('added');
        },
    .
    .
</script>

我们最后来测试一下:
file

本文章首发在 Laravel China 社区
上一篇 下一篇
讨论数量: 1
发起讨论


zh117
关于 37 节的遗漏?
0 个点赞 | 0 个回复 | 问答
刻意练习,每日精进。
4
点赞
210
浏览
1
讨论

维护者
18
18