Jhecht
Jhecht

Reputation: 4435

Anyway to use data properties in text passed to slot?

I have a Vue setup like so:

Vue.component('custom-component', {
  template: '<div><slot></slot></div>',
  data: function() {
    return {
      parent_data: [1, 2, 3, 4, 5]
    }
  }
});

Vue.component('sub-component', {
  props: {
    dataProp: {
      default: []
    }
  },
  data: function() {
    return {
      data: []
    }
  },
  template: '<div class="subs">{{data.length}}<slot></slot></div>',
  mounted: function() {
    this.data = this.dataProp;
  }
});

new Vue({
  el: '#root'
});
<script src="https://unpkg.com/[email protected]"></script>
<div id="root">
  <custom-component>
    hello
    <sub-component>
      sub component hello
    </sub-component>
  </custom-component>
</div>

The parent_data property is actually defined via Vue Resource in an ajax call, though it does not appear that this has anything to do with that.

You'll see that we get "hello 0 sub component hello" output in the browser. Okay, cool. So I figured I would fiddle around with it and try and put some of the text into the component's text slot, like so:

Vue.component('custom-component', {
  template: '<div><slot></slot></div>',
  data: function() {
    return {
      parent_data: [1, 2, 3, 4, 5]
    }
  }
});

Vue.component('sub-component', {
  props: {
    dataProp: {
      default: []
    }
  },
  data: function() {
    return {
      data: []
    }
  },
  template: '<div class="subs"><slot></slot></div>',
  mounted: function() {
    this.data = this.dataProp;
  }
});

new Vue({
  el: '#root'
});
<script src="https://unpkg.com/[email protected]"></script>
<div id="root">
  <custom-component>
    hello
    <sub-component>
      {{data.length}}sub component hello
    </sub-component>
  </custom-component>
</div>

But this no longer works how I expected it to. What do I have to do in order to get this example to work?

The more-close-to-reality portion of this question looks something like this:

Vue.component('custom-component', {
  template: '<div><slot></slot></div>',
  data: function() {
    return {
      parent_data: [1, 2, 3, 4, 5]
    }
  },
  mounted: function() {
    //this.$http.get('/page/here').then(results=> this.parent_data = results, console.error );
  }
});

Vue.component('sub-component', {
  props: {
    dataProp: {
      default: []
    }
  },
  data: function() {
    return {
      data: []
    }
  },
  template: '<div class="subs"><slot></slot></div>',
  mounted: function() {
    this.data = this.dataProp;
  }
});

new Vue({
  el: '#root'
});
<script src="https://unpkg.com/[email protected]"></script>
<div id="root">
  <custom-component>
    hello
    <sub-component :data-prop="parent_data">
      {{data.length}} sub component hello
    </sub-component>
  </custom-component>
</div>

Upvotes: 2

Views: 465

Answers (1)

Bert
Bert

Reputation: 82489

If you want to reference data from the child inside the slot, you need to use scoped slots.

Here is your last example updated to use scoped slots.

console.clear()

Vue.component('custom-component', {
  template: '<div><slot :parent_data="parent_data"></slot></div>',
  data: function() {
    return {
      parent_data: [1, 2, 3, 4, 5]
    }
  },
  mounted: function() {
    //this.$http.get('/page/here').then(results=> this.parent_data = results, console.error );
  }
});

Vue.component('sub-component', {
  props: {
    dataProp: {
      default: []
    }
  },
  data: function() {
    return {
      subData: this.dataProp
    }
  },
  template: '<div class="subs"><slot :sub-data="subData"></slot></div>',
  mounted: function() {
    console.log(this.dataProp)
  }
});

new Vue({
  el: '#root'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="root">
  <custom-component>
    <template scope="{parent_data}">
      hello
      <sub-component :data-prop="parent_data">
        <template scope="props">
          {{props.subData.length}} sub component hello
        </template>
      </sub-component>
    </template>
  </custom-component>
</div>

Essentially, you expose the data you want to be able to use in the child's slot as a property on the slot,

<slot :parent_data="parent_data"></slot>

and then you have to wrap the content you want to use in the slot with a template that has the keyword scope.

<template scope="props">
  {{props.subData.length}} sub component hello
</template>

The expression in the scope property of template defines how you will access the data from the child. In the example just above, I used scope="props" which will mean that all the properties passed on the slot will be available as properties of props. You can also use object destructuring inside your scope expression to just get the properties you want to use, as in scope="{parent_data}".

Upvotes: 1

Related Questions