从 VueJS 的父组件中引用子组件的索引

Posted

技术标签:

【中文标题】从 VueJS 的父组件中引用子组件的索引【英文标题】:Referencing the index of a child component from within a parent component in VueJS 【发布时间】:2019-08-10 13:15:06 【问题描述】:

我正在处理 Laravel 5.7 应用程序中的一个页面,该应用程序有一系列 VueJS 子组件 "Prospect" 包裹在一个VueJS 父组件 “Prospects”。子组件是表行,每个行都有一个用于删除行/子项的按钮。父组件有一个删除多个子组件的按钮。

但是我不确定如何让父组件函数调用子组件函数来删除已标记为删除的子组件。

从子组件本身中一次删除一个子组件可以正常工作。但是,我试图从父组件函数 deleteSelectedProspects() 内的循环中多次调用相同的子组件删除函数。

deleteSelectedProspects() 我需要访问分配给子组件的 index,以便引用 $refs 中的正确行强> 数组。


如何访问子组件的索引,以便在 selectedBoxes.forEach 循环内的 $refs 数组中正确引用它?


包含 Prospects 父组件的刀片视图的源代码:

@extends('layouts.admin')

@section('content')
    <section id="widget-grid">
        <div class="row">
            <article class="col-xs-12 col-sm-12 col-md-12 col-lg-12 sortable-grid ui-sortable">
                <prospects inline-template class="">
                <div>


                    <div class="jarviswidget jarviswidget-sortable" id="wid-id-1" data-widget-editbutton="false" data-widget-deletebutton="false">
                        <header class="ui-sortable-handle">
                            <div class="widget-header"> 
                                <h2><i class="fa fa-list text-orange"></i> Prospects/Leads List</h2>
                            </div>

                            <button class="btn btn-sm btn-primary d-none" @click="markSelectedProspectsContacted" id="btnMarkSelectedProspectsContacted">
                                <span class="fa fa-check-square-o"></span> Mark Selected as Contacted
                            </button>
                            <button class="btn btn-sm btn-danger d-none" @click="deleteSelectedProspects" id="btnDeleteSelectedProspects"><span class="fa fa-trash"></span> Delete Selected</button>
                        </header>

                        <!-- widget div-->
                        <div role="content">
                            <!-- widget content -->
                            <div class="widget-body">
                                <table id="prospectsTable" class="table table-striped table-bordered table-hover">
                                    <thead>
                                        <tr>
                                            <th>
                                                <label class="vcheck">
                                                    <input type="checkbox" id="toggleCheckAllProspects" value="1" /> <span></span>
                                                </label>
                                            </th>
                                            <th>Name</th>
                                            <th>Food Category</th>
                                            <th>Contact</th>
                                            <th>Admin Comments</th>
                                            <th></th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        <tr is="prospect"
                                            v-for="(prospect, index) in prospects" 
                                            :prospect="prospect" 
                                            :index="index" 
                                            :key="prospect.id" 
                                            ref="refIds" 
                                            @delete-prospect="removeProspect()" 
                                            @update-contacted="updateContacted(index)">
                                        </tr>
                                    </tbody>
                                </table>
                            </div>
                            <!-- end widget content -->
                        </div>
                    </div>
                    <!-- end jarviswidget div -->
                </div>
                </prospects>
            </article>
        </div>
    </section>
@endsection

@push('js-block')
    <script>
    $.widget( "ui.tabs", $.ui.tabs, 
        _getPanelForTab: function( tab ) 
            var id = $( tab ).attr( "aria-controls" );
            return $( this._sanitizeSelector( "#" + id ) );
        
    );

    $(function() 
        $('#prospectTabs').tabs(
            create: removeLoader($('#prospectTabs'))
        );

        $('#prospectTabsAdd').click(function() 
            $('#add-tab').removeClass('d-none');
        );

        $('#toggleCheckAllProspects').click(function() 
            $('.prospect-check').prop('checked', ($(this).is(':checked') ? true : false));
        );
    );
    </script>
@endpush

父组件源代码“prospects”

<script>
    export default 
        data: function() 
            return 
                formProspect: 
                    inputs: 
                        name:           '',
                        food_cat_id:    '', 
                        phone:          '',
                        website:        '',
                        contact_fname:  '',
                        contact_lname:  '',
                        contact_title:  '',
                        address:        '', 
                        address2:       '',
                        city:           '', 
                        state_id:       '',
                        zip:            '', 
                        response_notes: ''
                    ,
                    headerText: "<i class='fa fa-plus-square-o text-orange'></i> Add Prospect / Lead",
                    btnText: "<i class='fa fa-plus-square-o'></i> Add Prospect / Lead", 
                    errors: false
                ,
                formSearchProspect: 
                    inputs: 
                        keywords:   '',
                        contacted:  '', 
                        location:   '', 
                        radius:     5
                    
                , 
                formProspectMode: 'add',
                prospects: []
            
        , 
        computed: 

        ,
        watch: 
            formProspectMode: function(newMode) 
                switch(newMode) 
                    case 'add':
                        this.formProspect.headerText = "<i class='fa fa-plus-square-o text-orange'></i> Add Prospect / Lead";
                        this.formProspect.btnText = "<i class='fa fa-plus-square-o'></i> Add Prospect / Lead";
                        this.clearFormProspect();
                        break;

                    case 'edit':
                        this.formProspect.headerText = "<i class='fa fa-edit text-orange'></i> Edit Prospect / Lead";
                        this.formProspect.btnText = "<i class='fa fa-save'></i> Save Prospect / Lead";
                        break;
                
            

        ,

        methods: 
            fetchProspects() 
                var self = this;
                $.get('/prospect/fetch', function(r) 
                    if(r.successMsg) 
                        self.prospects = r.prospects;
                    
                )
            ,

            searchProspects() 
                var self = this;

                var params = 
                    'keywords'  : this.formSearchProspect.inputs.keywords, 
                    'contacted' : this.formSearchProspect.inputs.contacted
                ;

                if(this.formSearchProspect.inputs.location) 
                    params.location     = this.formSearchProspect.inputs.location;
                    params.radius       = this.formSearchProspect.inputs.radius;
                

                $.get('/prospect/search', params, function(r) 
                    if(r.successMsg) 
                        self.prospects = r.prospects;
                    
                )
            , 

            addProspect() 
                var self = this;

                $.ajax(
                    type:       "POST",
                    dataType:   "json", 
                    async:      false,
                    url:        "/prospect/add", 
                    data:       
                        name:                   this.formProspect.inputs.name,
                        food_cat_id:            this.formProspect.inputs.food_cat_id,
                        phone:                  this.formProspect.inputs.phone,
                        website:                this.formProspect.inputs.website,
                        contact_fname:          this.formProspect.inputs.contact_fname,
                        contact_lname:          this.formProspect.inputs.contact_lname,
                        contact_title:          this.formProspect.inputs.contact_title,

                        address:                this.formProspect.inputs.address, 
                        address2:               this.formProspect.inputs.address2, 
                        city:                   this.formProspect.inputs.city,
                        state_id:               this.formProspect.inputs.state_id, 
                        zip:                    this.formProspect.inputs.zip,
                        response_notes:         this.formProspect.inputs.response_notes
                    , success(r) 
                        if(r.successMsg) 
                            var newProspect = self.formProspect.inputs;
                            newProspect.id = r.newId;
                            newProspect.state = r.state;
                            newProspect.food_cat = r.food_cat;

                            console.log(newProspect);

                            self.prospects.push(Object.assign(, newProspect));

                            self.clearFormProspect();   
                         else if(r.errors) 
                            self.formProspect.errors = r.errors; 
                        
                           
                );
            ,

            saveProspect() 
                var self = this;

                $.post('/prospect/edit', 
                    id:                     this.formProspect.inputs.id,
                    name:                   this.formProspect.inputs.name,
                    food_cat_id:            this.formProspect.inputs.food_cat_id,
                    phone:                  this.formProspect.inputs.phone,
                    website:                this.formProspect.inputs.website,
                    contact_fname:          this.formProspect.inputs.contact_fname,
                    contact_lname:          this.formProspect.inputs.contact_lname,
                    contact_title:          this.formProspect.inputs.contact_title,

                    address:                this.formProspect.inputs.address, 
                    address2:               this.formProspect.inputs.address2, 
                    city:                   this.formProspect.inputs.city,
                    state_id:               this.formProspect.inputs.state_id, 
                    zip:                    this.formProspect.inputs.zip, 
                    response_notes:         this.formProspect.inputs.response_notes
                , function(r)  
                    if(r.successMsg) 
                        var savedProspect = self.prospects.filter(prospect => prospect.id == self.formProspect.inputs.id)[0];
                        savedProspect = Object.assign(, self.formProspect.inputs);

                        self.formProspectMode = "add";
                     else if(r.errors) 
                        self.formProspect.errors = r.errors; 
                    
                );
            ,

            removeProspect(index) 
                this.prospects.splice(index, 1);
            , 

            clearFormProspect() 
                this.formProspect.inputs = ;
                this.formProspect.errors = false;
            , 

            deleteSelectedProspects() 
                var self = this;
                var selectedBoxes = self.prospects.filter(prospect => prospect.selected); 

                $.SmartMessageBox(
                    title: "<i class='fa fa-trash text-orange-dark'></i> Delete (" + selectedBoxes.length + ") Selected Prospects",
                    content: "Are you sure you want to delete the selected leads?",
                    buttons: "[No][Yes]"
                , function(e) 
                    if("Yes" == e) 
                        selectedBoxes.forEach(function(p) 
                            var lostChild = self.prospects.filter(prospect => prospect.id == p.id);

                            // HOW CAN I ACCESS THE INDEX OF THE LOST CHILD TO REFERENCE IT IN THE $refs ARRAY BELOW
                            // HOW CAN I ACCESS THE INDEX OF THE LOST CHILD TO REFERENCE IT IN THE $refs ARRAY BELOW
                            //self.$refs.refIds[lostChildIndex].deleteProspect();
                        );
                    
                );
            , 

            markSelectedProspectsContacted() 
                var self = this;
                var selectedBoxes = self.prospects.filter(prospect => prospect.selected); 

                $.SmartMessageBox(
                    title: "<i class='fa fa-trash text-orange-dark'></i> Mark (" + selectedBoxes.length + ") Selected Prospects as Contacted",
                    content: "Are you sure you want to mark the selected leads as contacted?",
                    buttons: "[No][Yes]"
                , function(e) 
                    if("Yes" == e) 
                        selectedBoxes.forEach(function(p) 

                            /*
                            mark contacted
                            */
                        );
                    
                );
            , 

            updateRadiusSearchDiv() 
                if($('#searchLocation').val()) 
                    $('#radiusSearchDiv').removeClass('d-none');
                 else 
                    $('#radiusSearchDiv').addClass('d-none');
                
            
        , 
        mounted() 
            this.fetchProspects();

            setTimeout(function() 
                $('#prospectsTable').DataTable(
                    "columnDefs": [
                         "orderable": false, "targets": [0,4,5] 
                    ], 
                    "language": 
                        "search": "Filter Results:" 
                    , 
                    "dom": "ftilp"
                );

                $('#prospectsTable > thead > tr th:first-child').removeClass('sorting_asc');
            , 500);

            $('#btnDeleteSelectedProspects, #btnMarkSelectedProspectsContacted').removeClass('d-none');
        
    

</script>

子组件“prospect”的源码:

<template>
    <transition name="fade">
        <tr v-if="show">
            <td class="text-center align-middle">
                <label class="vcheck">
                    <input type="checkbox" class="prospect-check" value="1" v-model="prospect.selected" /> <span></span>
                </label>
            </td>
            <td>
                <div> prospect.name </div>
                <div v-show="prospect.contacted" class="label label-success"><span class="fa fa-check-square-o"></span> Contacted!</div>
            </td>
            <td> prospect.food_cat.title </td>
            <td> prospect.contact_fname <span v-if="prospect.contact_lname">  prospect.contact_lname </span><span v-if="prospect.contact_title">,  prospect.contact_title </span></td>
            <td> prospect.response_notes </td>
            <td class="text-right align-middle">
                <button class="btn btn-primary" @click="updateContacted" v-show="!prospect.contacted"><span class="fa fa-check-square-o"></span> Mark As Contacted</button>
                <button class="btn btn-primary" @click="editProspect"><span class="fa fa-edit"></span> Edit Lead</button>
                <button class="btn btn-danger" @click="removeProspect"><span class="fa fa-trash"></span> Delete Lead</button>
            </td>
        </tr>
    </transition>
</template>

<script>
    export default 
        props: ['prospect', 'index'],
        data: function() 
            return 
                prospectData: this.prospect, 
                show: true, 
                prospectIndex: this.index
            
        ,
        methods: 
            editProspect() 
                this.$parent.formProspect.inputs = this.prospectData;
                this.$parent.formProspectMode = "edit";

                $('#prospectTabs').tabs('option', 'active', 1);
                $('#add-tab').removeClass('d-none');

                window.scrollTo( top: 0, behavior: 'smooth' );
            ,
            updateContacted() 
                var self = this;

                $.SmartMessageBox(
                    title: "<i class='fa fa-trash text-orange-dark'></i> Mark as Contacted?",
                    buttons: "[No][Yes]"
                , function(e) 
                    if("Yes" == e) 
                        $.post('/prospect/updateContacted/' + self.prospectData.id, function(r) 
                            if(r.successMsg) 
                                self.$emit('update-contacted');
                            
                        );
                    
                );
            , 
            removeProspect() 
                var self = this;
                $.SmartMessageBox(
                    title: "<i class='fa fa-trash text-orange-dark'></i> Delete Prospect/Lead",
                    content: "Are you sure you want to delete this prospect/lead?",
                    buttons: "[No][Yes]"
                , function(e) 
                    if("Yes" == e) 
                        self.deleteProspect();
                    
                );
            , 
            deleteProspect() 
                var self = this;

                $.post('/prospect/delete/' + self.prospectData.id, function(r) 
                    if(r.successMsg) 
                        self.show = false;
                        self.$emit('delete-prospect');
                    
                );
            
        ,
        mounted() 

           
    
</script>

【问题讨论】:

【参考方案1】:

您的代码有点混乱,所以这只是一个应该可以正常工作的想法。你不应该碰 refs,你只需要改变数据模型。从数据中移除,组件将自行移除。

在父组件中添加一个名为 checkProspect 的方法

data: 
   ...
   checkedProspects: []
,
methods: 
   checkProspect(prospectId)
      checkedProspects.push(prospectId);
   ,
   deleteCheckedProspects()
     // Remove from the model by checkedProspects ids
   

然后添加处理程序:

<tr is="prospect"
   v-for="(prospect, index) in prospects" 
   :prospect="prospect" 
   :index="index" 
   :key="prospect.id" 
   ref="refIds" 
   @checkProspect="checkProspect" 
   @delete-prospect="removeProspect()" 
   @update-contacted="updateContacted(index)">

在子组件中,点击查看时,触发事件:

this.$emit('checkProspect', this.id);

【讨论】:

感谢关于不使用 $refs 的有用提示,但是使用这种方法仍然存在同样的问题,因为我在父组件中留下了一组 id 来删除子组件.为了修复它,我需要将索引与单击复选框时调用的函数一起传递,以便将它们从父组件中删除。 @McWayWeb 如果你有前景.id 你不需要知道索引。您可以使用普通循环或一些库(如 lodash)找到选定的潜在客户。

以上是关于从 VueJS 的父组件中引用子组件的索引的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法从VueJS中的父级触发组件方法?

无法从Vuejs中的子组件向父组件发出事件

如何在 VueJs 组件中正确引用 HTMLElement

修改子组件中的选择值后触发函数 - VueJs

如何从Angular 2中的子组件事件触发父组件中的本地引用?

子视图组件将道具传递给Vue中的父布局