最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Correct way to pass props to all children through slot-scope in Vue.js - Stack Overflow

programmeradmin0浏览0评论

Updated code in light of ments about slot-scope

Coming from React, I am having a hard time understanding how Vue uses slots and slot-scope to pass props to child ponents.

In my current project, I have a DataSlicer ponent that receives a data prop and performs some manipulation on it. I would then like to pass the manipulated data on to child ponents via <slot>. I understand that slot-scope is the way to do this but it seems to only work when the parent ponent tells a child to pass something to its own children -- which seems to defeat the whole point (in terms of position and separation of concerns).

This is what I have working right now, based on reading How to pass props using slots from parent to child -vuejs

App.vue

<DataSlicer
    v-if="data"
    y-axis-dimension="Subparameter"
    x-axis-dimension="Year"
    value-dimension="Total_Registrations"
    :filters="{}"
    :data="data"
>
    <template slot-scope="childProps">
        <Chart :title="title" :series-data="childProps.slicedData" />
    </template>
</DataSlicer>

DataSlicer.vue

<template>
  <div>
    <slot :slicedData="slicedData"/>
  </div>
</template>

My expectation was that I could define slot-scope on the <template> tag in DataSlicer.vue, but that doesn't seem to work.

Am I missing something? I don't understand why App.vue would need to know or care what DataSlicer is passing to its children. The problem is only pounded the more I split up my ponents (for example, if I stick DataSlicer inside another ponent to abstract the api calls, which are currently handled at the App.vue level, then App.vue also has to also tell that ponent to pass data on to its DataSlicer child[ren]).

Maybe I am just stuck in React thinking here and there's a different or better way to do this?

EDIT:

If it helps, I'm able to acplish what I want using a render function like so:

render: function (createElement) {
    return createElement(
      'div',
      this.$slots.default.map(vNode => {
        if (vNodeponentOptions) {
          vNodeponentOptions.propsData.slicedData = this.slicedData;
        }
        return vNode;
      })
    )
  }

This feels rather hackish/fragile and also like I'm stretching Vue to do something in the "react way" when there is probably a better approach.

EDIT 2:

After more research and experimentation, I have also tried the provide/inject approach. This works (by using defineObjectProperty to bind a dynamic getter for the property being passed) but it means child ponents have to be explicitly written to accept provided props rather than just accepting the prop whether it's provided by a parent ponent or directly.

I've also tried using dynamic ponents and v-for (<ponent>), but it ends up being really messy as I am essentially re-initializing the already-defined ponents received in $slots.default as dynamic ones -- it also introduces some recursive weirdness when I want these ponents to also have children, and it's hard to deal with non-ponent children.

This is a very mon pattern in React and trivial to implement, so I'm still curious if there is another way of acplishing this that is more in line with the Vue way of doing things.

I am trying to build ponents that are reusable, self-contained, and posable, so having to specify slot-scope in a <template> tag in the parent ponent each time these are used together doesn't really make sense for my purposes. My child ponents should not be concerned with where their props e from, and my parent ponents should not be concerned with what props their children are passing on to their own children.

Updated code in light of ments about slot-scope

Coming from React, I am having a hard time understanding how Vue uses slots and slot-scope to pass props to child ponents.

In my current project, I have a DataSlicer ponent that receives a data prop and performs some manipulation on it. I would then like to pass the manipulated data on to child ponents via <slot>. I understand that slot-scope is the way to do this but it seems to only work when the parent ponent tells a child to pass something to its own children -- which seems to defeat the whole point (in terms of position and separation of concerns).

This is what I have working right now, based on reading How to pass props using slots from parent to child -vuejs

App.vue

<DataSlicer
    v-if="data"
    y-axis-dimension="Subparameter"
    x-axis-dimension="Year"
    value-dimension="Total_Registrations"
    :filters="{}"
    :data="data"
>
    <template slot-scope="childProps">
        <Chart :title="title" :series-data="childProps.slicedData" />
    </template>
</DataSlicer>

DataSlicer.vue

<template>
  <div>
    <slot :slicedData="slicedData"/>
  </div>
</template>

My expectation was that I could define slot-scope on the <template> tag in DataSlicer.vue, but that doesn't seem to work.

Am I missing something? I don't understand why App.vue would need to know or care what DataSlicer is passing to its children. The problem is only pounded the more I split up my ponents (for example, if I stick DataSlicer inside another ponent to abstract the api calls, which are currently handled at the App.vue level, then App.vue also has to also tell that ponent to pass data on to its DataSlicer child[ren]).

Maybe I am just stuck in React thinking here and there's a different or better way to do this?

EDIT:

If it helps, I'm able to acplish what I want using a render function like so:

render: function (createElement) {
    return createElement(
      'div',
      this.$slots.default.map(vNode => {
        if (vNode.ponentOptions) {
          vNode.ponentOptions.propsData.slicedData = this.slicedData;
        }
        return vNode;
      })
    )
  }

This feels rather hackish/fragile and also like I'm stretching Vue to do something in the "react way" when there is probably a better approach.

EDIT 2:

After more research and experimentation, I have also tried the provide/inject approach. This works (by using defineObjectProperty to bind a dynamic getter for the property being passed) but it means child ponents have to be explicitly written to accept provided props rather than just accepting the prop whether it's provided by a parent ponent or directly.

I've also tried using dynamic ponents and v-for (<ponent>), but it ends up being really messy as I am essentially re-initializing the already-defined ponents received in $slots.default as dynamic ones -- it also introduces some recursive weirdness when I want these ponents to also have children, and it's hard to deal with non-ponent children.

This is a very mon pattern in React and trivial to implement, so I'm still curious if there is another way of acplishing this that is more in line with the Vue way of doing things.

I am trying to build ponents that are reusable, self-contained, and posable, so having to specify slot-scope in a <template> tag in the parent ponent each time these are used together doesn't really make sense for my purposes. My child ponents should not be concerned with where their props e from, and my parent ponents should not be concerned with what props their children are passing on to their own children.

Share Improve this question edited Feb 4, 2019 at 15:38 stuff and things asked Feb 1, 2019 at 10:12 stuff and thingsstuff and things 1332 silver badges10 bronze badges 2
  • In DataSlicer.vue <slot :slicedData="slicedData"/> is defining the property 'slicedData' on the slot scope. In App.vue, <template slot-scope="{ slicedData }"> is assigning the slot scope to a variable named "{ slicedData }". Does this code work at all? – Andrew Castellano Commented Feb 1, 2019 at 13:43
  • @AndrewCastellano Hi Andrew, yes this code works - somewhere I had picked up the idea that object destructuring syntax could be used to pass what I wanted, but now I think I understand that slot-scope is just the namespace. I've updated my code to make this clearer (this doesn't affect the underlying question of why the parent ponent should handle this). – stuff and things Commented Feb 1, 2019 at 14:23
Add a ment  | 

1 Answer 1

Reset to default 4

I was finally able to do what I wanted using the new v-slot scope in Vue 2.6, described here: https://github./vuejs/vue/issues/9306

Part of my issue was that the Vue team sees the "state provider" approach I was going for as an anti-pattern because it's not clear where props are ing from. I see their point and I think the new syntax provides a good balance between being clear and avoiding the excessive boilerplate of <template> tags wrapping the child ponents. It also simplifies and makes explicit the ability for a ponent to take both provided props and "regular" props without caring where they e from (my problem with provide/inject).

The final result looks something like this:

App.vue

<DataGetter {...other props} v-slot="{ data }">
      <DataSlicer {...other props} :data="data" v-slot="{ slicedData }">
        <!-- Using provided data prop -->
        <Chart :data="slicedData" />
        <!-- Data prop explicitly defined -->
        <Chart :data="[ 1, 2, 3 ]" />
      </DataSlicer>
</DataGetter>

DataGetter and DataSlicer render functions:

render(h) {
    if (this.$scopedSlots.default) {
      return h(
        'div',
        this.$scopedSlots.default({ data: this.data })
      )
    }
  }

I hope this is helpful for anyone else ing from React. I'll leave this question open in case someone else es along with a better answer.

发布评论

评论列表(0)

  1. 暂无评论