Vue.js 将数据从插槽传递到组件实例

Posted

技术标签:

【中文标题】Vue.js 将数据从插槽传递到组件实例【英文标题】:Vue.js pass data from slot into component instance 【发布时间】:2017-07-13 19:31:38 【问题描述】:

我正在尝试构建一个可重用的组件来处理使用 Vue.js 通过 AJAX 提交表单。理想情况下,我想做的是有一个通用组件可以用来代替 html form 元素,其中可能包含一组未知的表单元素,例如 inputselecttextarea 和以此类推。

我的组件有以下代码,名为ajax-form

<template>
    <form class="form" :action="action" :method="method" v-on:submit.prevent="ajaxSubmit">
        <slot></slot>
    </form>
</template>

<script>
module.exports = 
    props: 
        action: 
            required: true,
            type: String
        ,
        method: 
            default: 'post',
            required: false,
            type: String
        
    ,
    data() 
        return 
            formData: 
        
    ,
    methods: 
        ajaxSubmit() 
            // Do ajax
        
    

</script>

在我的 HTML 中,我会有如下内容:

<ajax-form action="http://example.com/do/something">
    <input name="first_name" type="text">
    <textarea name="about_you"></textarea>
</ajax-form>

理想情况下,我希望使用它的插槽将放置在我的组件内的所有表单元素映射到我的 Vue 组件实例中的 data.formData 属性。所以在这种情况下,data 属性看起来像:

data: 
    formData: 
        first_name: '',
        about: ''
    

如果我要在我的 HTML 中向组件添加另一个字段,我希望它也能映射到 Vue 实例的 data 属性。

有什么方法可以实现吗?有没有办法告诉 Vue,当我通过插槽将我的表单元素放入组件时,我希望这个元素映射到组件的 data 中的某个东西?

我尝试在每个表单元素上添加v-modelv-bind,看看它是否会以某种方式将数据传递到组件的数据中:

<ajax-form action="http://example.com/do/something">
    <input name="first_name" type="text" v-model="formData.first_name">
</ajax-form>

然而,Vue 抱怨必须在模板中使用响应式数据属性之前声明它们:

[Vue 警告]:属性或方法“formData”未在实例上定义,但在渲染期间被引用。确保在 data 选项中声明响应式数据属性。

【问题讨论】:

只是大声思考:我想知道您是否可以使用mounted (vuejs.org/v2/api/#mounted) 方法获取对 Vue 创建的 DOM 元素的引用,然后遍历它的子元素(使用 jQuery ?) 并通过Vue.set (vuejs.org/v2/api/#Vue-set) 为this.data 添加反应属性。 看看这支笔codepen.io/anon/pen/evYzOv?editors=1011 谢谢你们的建议。 @PatrickSteele 我尝试了您的建议,但无法再进一步。我开始认为这是不可能的。文档似乎在这里说明了这一点:vuejs.org/v2/guide/…。 @ShekharJoshi 我不确定你是否理解我的问题。我无法提前知道插槽的内容,我希望能够在通过插槽传递到我的组件的元素和我的data 的项目之间创建反应绑定。您的建议假设我提前知道数据等将是什么。 【参考方案1】:

由于插槽范围仅限于父范围,因此您无法执行您的要求。

您可以在全局 Vue 实例上定义 formData,因此现在可以从父范围访问它。

const app = new Vue(
    el: '#app',
    data: 
        formData : 
    
);

现在您可以将其作为道具传递给表单:

<ajax-form action="#" :form-data="formData>
    <input name="first_name" type="text" v-model="formData.first_name">
</ajax-form>

【讨论】:

完美。我感觉 Vue 不会让我按照我想要的方式做,只需要一个组件从插槽中传递数据,但这是一个很好的解决方法。 这个问题也可以通过使用作用域槽很好地解决。没有任何黑客。如blockhead's answer 中所述【参考方案2】:

您可以使用 scopedSlots 做到这一点。

API 如下所示:

<ajax-form action="http://example.com/do/something">
   <template scope="formData">    
   <input name="first_name" type="text" v-model="formData.first_name">
   </template>
</ajax-form>

ajax-form 组件中:

<form class="form" :action="action" :method="method" v-on:submit.prevent="ajaxSubmit">
   <slot :formData="formData">
</form>

【讨论】:

这帮助很大!谢谢。 这应该是公认的答案!这确实帮助我理解了作用域插槽,因为文档没有给出任何真实世界的例子。另外,请注意,自 Vue 2.5 起,scope 属性已被弃用,取而代之的是slot-scope,它可以用于任何元素,而不仅仅是template。因此,在本例中,您可以使用 &lt;input name="first_name" type="text" v-model="formData.first_name" slot-scope=" formData "&gt; 而不是将输入包装在模板标签中。 非常感谢。但是 vue 2.6 更新了使用范围槽的语法。【参考方案3】:

您可以将 parent 的数据作为参数传递给 child,如下所示:

在父级中:

<child-component ref="child">
      <h1 slot="heading">Test passing data to child</h1>
      <form slot="content">
          <input type="text" v-model="name" placeholder="Type your name" class="half">
          <input type="text" v-model="email" placeholder="Type your email" class="half">
          <div class="actions">
              <button data-role="submit" @click.prevent="callAddRep($event)">Add</button>
          </div>
      </form>
</child-component>
data() 
    return 
        name: '',
        email: ''
    
,
methods: 
    callAddRep: function(e) 

      let name = this.name,
          email = this.email

      this.$refs.child.addRep(e, 
          name,
          email
      )
  

在孩子身上:

methods: 
      addRep: function(e, data) 

          //Data passed successfully
          console.log(data)

      
  

【讨论】:

【参考方案4】:

虽然@blockhead 的回答是正确的,但需要注意的是,插入 ajax-form 的插槽是否是具有自己模板标签的单个文件组件并不重要。您仍然必须将该组件包装在外部模板标签中以拦截插槽属性,然后将它们作为属性传递给您的实际目标组件

<modal-two modal_id="notif-modal">

    <template #default="id_for_children">

        <notification-alert :parent_modal_name="id_for_children" />
    </template>
</modal-two>

我没有在其他地方看到这个

【讨论】:

以上是关于Vue.js 将数据从插槽传递到组件实例的主要内容,如果未能解决你的问题,请参考以下文章

如何将数据从 Vue 实例传递给嵌套组件

如何将数据从 Vue 实例传递给嵌套组件

将插槽传递到 Vue.js 中的插槽

Vue.js 3 - 将组件插入插槽

将父母道具传递给Vue,js中的插槽

将 props 传递给 Vue-router 实例化的 Vue.js 组件