1

I've created a simple component:
https://jsfiddle.net/s08yhcda/1/

<div id="app">
  <button-counter>My counter: <span v-text="count"></span></button-counter>
</div>

// Define a new component called button-counter
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++"><slot></slot></button>'
})

// boot up the demo
var demo = new Vue({
  el: '#app'
})

Yet I cannot bind to the inner component data (counter). How can a component expose its data to the <slot> scope? I know the "events up, props down" idea. But still I thought it would be possible to bind component data inside its scope (inside the <button-counter> element)

I prefer not using event for something that simple. Any other way?

1
  • Where you try to access the counter variable isn't inside button counter, it inside #app Commented Apr 28, 2018 at 22:01

2 Answers 2

4

There are multiple ways to solve this problem:

Using events

This is usually the best method, as it is expected by other Vue developer

// Define a new component called button-counter
Vue.component('button-counter', {
    data: function () {
        return {
            count: 0
        }
    },
    methods: {
        click() {
            this.count++;
            this.$emit('input', this.count);
        },
    },
    template: '<button v-on:click="click"><slot></slot></button>'
})

// boot up the demo
var demo = new Vue({
    data() {
        return {
            count: 0,
        };
    },
    el: '#app'
});
<!-- development version, includes helpful console warnings -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<div id="app">
  <button-counter @input="count = $event">My counter: <span v-text="count"></span></button-counter>
</div>

Using refs:

// Define a new component called button-counter
Vue.component('button-counter', {
    data: function () {
        return {
            count: 0
        }
    },
    methods: {
        click() {
            this.count++;
        },
    },
    template: '<button v-on:click="click"><slot></slot></button>'
})

// boot up the demo
var demo = new Vue({
    data() {
        return {
            mounted: false,
        };
    },
    mounted() {
        this.mounted = true;
    },
    computed: {
        count() {
            return this.mounted ? this.$refs.counter.count : 0;
        },
    },
    el: '#app'
});
<!-- development version, includes helpful console warnings -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<div id="app">
  <button-counter ref="counter">My counter: <span v-text="count"></span></button-counter>
</div>

Using slot-scope

Using slot scope, you can pass multiple arguments to the parent slot:

// Define a new component called button-counter
Vue.component('button-counter', {
    data: function () {
        return {
            count: 0
        }
    },
    methods: {
        click() {
            this.count++;
        },
    },
    template: '<button v-on:click="click"><slot :count="count"></slot></button>'
})

// boot up the demo
var demo = new Vue({
    el: '#app'
});
<!-- development version, includes helpful console warnings -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<div id="app">
    <button-counter>
        <span slot-scope="prop">
            My counter:
            <span v-text="prop.count"></span>
        </span>
    </button-counter>
</div>

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, unfortunately I don't like these ways, I would probably have to find a hack, or to resort to using events. I found a library 'vee-validate' which actually does push data to the parent component, any idea how it is done?
@user3599803 They migth do it from the parent by looking over $children or from the child by walking the chain of $parent
I can't figure out how it is possible, vee-validate actually injects a reactive property to a vue instance, couldn't find how it's done
1

You can send data to the slot. Look at this example:

Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++"><slot :count="count" /></button>'
})

<div id="app">
  <button-counter>
    <template scope="props">
      My counter: <span>{{ props.count }}</span>
    </template>
  </button-counter>
</div>

https://jsfiddle.net/s08yhcda/2/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.