自定义事件

1、 使用背景

接着上一章讲的slot插槽的页面。

<body>

<div id="app">
    <todo>
        <todo-title slot="todo-title" :tit="title"></todo-title>
        <todo-items slot="todo-items" :itm="i" v-for="i in todoItems"></todo-items>
    </todo>
</div>

<script>
    Vue.component('todo', {
        template: '<div>\
                    <slot name="todo-title"></slot>\
                      <ul>\
                        <slot name="todo-items"></slot>\
                      </ul>\
                    </div>'
    });

    Vue.component('todo-title',{
        props: ['tit'],
        template: '<div>{{tit}}</div>'
    });
    Vue.component('todo-items',{
        props: ['itm'],
        template: '<li>{{itm}} <button>删除</button></li>'
    });
    var vm = new Vue({
        el: "#app",
        data: {
            title: "学习列表",
            todoItems: ['Java','前端','Linux']
        }
    });
</script>

</body>

我在每个item后面都添加一个button按钮,我想让每一行后面点击删除后就删除此行。

在这里插入图片描述

组件和组件之间可以用插槽。但是组件想要调用Vue实例里面的方法,还得把data中的某个数据删除?

实际上,组件自身可以写methods方法。

<script>
	Vue.component('todo-items',{
        props: ['itm'],
        template: '<li>{{itm}} <button @click="remove">删除</button></li>',
        methods: {
            remove: function (){
                alert("111")
            }
        }
    });
</script>

在这里插入图片描述

说明这个事件绑定成功了。

关键是,我组件里的一个按钮,想要去删Vue实例的data中的某个数组中的某个数据,能删的到吗?

我们试试组件中的按钮能不能调用Vue实例中的methods

<script>
    Vue.component('todo-items',{
        props: ['itm'],
        template: '<li>{{itm}} <button @click="removeItem">删除</button></li>'
    });
    var vm = new Vue({
        el: "#app",
        data: {
            title: "学习列表",
            todoItems: ['Java','前端','Linux']
        },
        methods: {
            removeItem: function (){
                alert("123")
            }
        }
    });
</script>

经过测试,发现是无法调用的。是拿不到方法的。

在这里插入图片描述

但是不管你用哪里的方法,你总归是要操作Vue实例中data里面的todoItems那个数组中的数据的,这是毫无疑问的。

那我们来看看怎么样可以触发这件事情。

先不管谁来调用,怎么调用;先看看怎么来写一个方法能够删除数组中的元素。

 <script>
     var vm = new Vue({
         el: "#app",
         data: {
             title: "学习列表",
             todoItems: ['Java','前端','Linux']
         },
         methods: {
             removeItem: function (index){
                 console.log("删除了"+this.todoItems[index]+"OK");
                 this.todoItems.splice(index,1);    //一次删除一个元素
             }
         }
     });
</script>

这里用到了一个js对数组元素进行操作的splice()方法。

我们先直接在控制台执行这个方法,看看能否删除

在这里插入图片描述

在这里插入图片描述

这个方法本身逻辑是ok的。

那么接下来的问题就是,怎么样用组件中的事件去做这样一个方法呢?

首先,如果真的在某处调用了这一方法,那么先不管是在哪里调用的,肯定要传递一个index参数值。不要忘了v-for是可以遍历出index的。

<div id="app">
    <todo>
        <todo-title slot="todo-title" :tit="title"></todo-title>
        <todo-items slot="todo-items" v-for="(i,index) in todoItems" :itm="i" :indx="index"></todo-items>
    </todo>
</div>
<script>
    Vue.component('todo-items',{
        props: ['itm','indx'],
        template: '<li>{{indx}}----{{itm}} <button @click="">删除</button></li>'
    });
</script>

可以发现index是取到了的

在这里插入图片描述

我们组件中的button按钮的@click事件绑定的方法,永远都只能是组件内定义的methods方法。但是我们要想方设法的向Vue实例中methods定义的removeItems上靠拢。

<script>
    Vue.component('todo-items',{
        props: ['itm','indx'],
        template: '<li>{{indx}}----{{itm}} <button @click="remove">删除</button></li>',
        methods: {
            remove: function (){

            }
        }
    });
    var vm = new Vue({
        el: "#app",
        data: {
            title: "学习列表",
            todoItems: ['Java','前端','Linux']
        },
        methods: {
            removeItem: function (index){
                console.log("删除了"+this.todoItems[index]+"OK");
                this.todoItems.splice(index,1);    //一次删除一个元素
            }
        }
    });
</script>

2、 示例

这个问题通过Vue给我们提供的自定义事件就可以完成了。

通过以上代码不难发现,数据项在Vue的实例中,但是删除操作要在组件中完成,那么组件如何才能删除Vue实例中的数据呢?此时就涉及到参数传递与事件分发了,Vue为我们提供了自定义事件的功能,很好的帮我们解决了这个问题。使用this.$emit(‘自定义事件名’,参数)。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js"></script>
</head>
<body>

<div id="app">
    <todo>
        <todo-title slot="todo-title" :tit="title"></todo-title>
        <todo-items slot="todo-items" v-for="(i,index) in todoItems"
                    :itm="i" v-on:rem="removeItem(index)"></todo-items>
    </todo>
</div>

<script>
    Vue.component('todo-items',{
        props: ['itm'],
        template: '<li>{{itm}} <button @click="remove">删除</button></li>',
        methods: {
            remove: function (){
                this.$emit('rem')
            }
        }
    });
    var vm = new Vue({
        el: "#app",
        data: {
            title: "学习列表",
            todoItems: ['Java','前端','Linux']
        },
        methods: {
            removeItem: function (index){
                alert(index);
                this.todoItems.splice(index,1);    //一次删除一个元素
            }
        }
    });
</script>

</body>
</html>

捋一遍思路:

  • 首先,由于Vue实例是和#app绑定的。所以,处于#app中的自定义标签(组件),是完全可以通过v-on去绑定Vue实例中的事件的。于是有了v-on:rem=“removeItem”
  • 自定义标签与Vue实例绑定好了,然后这个组件标签想要与Vue.component中的事件建立绑定,如何完成。就使用到了this.$emit。
  • 在组件的methods中定义一个remove方法,这个方法中,通过this.$emit(‘rem’),最终实现了将组件中的remove方法,通过中间属性rem,与Vue实例中的removeItem真正的绑定了起来。在组件中调用remove就相当于调用Vue实例中的removeItem。
  • 下标index的传递逻辑为:首先v-for能够遍历出index(这个是v-for的一个默认的功能,index这个变量就是返回索引值的),然后在v-on绑定事件时,将事件的传参设为index。这样,事件的function就会传入真正的那个index下标。

注意:index下标只有在v-for遍历的时候才能取到,它才是真正的遍历数组中的那个数组元素下标,你在其他地方写index都是取不到的,而仅仅是当做一个普通为定义的变量去识别而已,还会告诉你undefined。

如果由于index传值问题导致最终传入this.todoItems.splice(index,1)这个方法中的index部分为“undefined”的话,你会发现数组元素也会一个一个地变少,只不过每次删除都只会移除数组中的第一个元素。

Logo

前往低代码交流专区

更多推荐