Martyn Ball
Martyn Ball

Reputation: 4885

Vue Component, toggle data

Think i'm cracking up, this is some very basic stuff but it doesn't seem to be working...

Basically clicking the link should toggle display between true and false, but this isn't the case.

Vue.component('dropdown', {
    props: [ 'expanded' ],
    data: function() {
      return {
        display: !!(this.expanded)
      }
    },
    template: '<div><transition name="expand"><slot :display="display"></slot></transition></div>'
  });
  window.app = new Vue({
   el: '#app'
  });
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
    <dropdown>
      <div slot-scope="{ display }">
        <a href="javascript:void(0)" @click="display = !display">Toggle {{ display }}</a>
        <div v-if="display">
          Dropdown content
        </div>
      </div>
  </dropdown>
</div>

Edit:

Updated code, I forgot I changed that, I did infact have the click event as display = !display. But even with that said, if you had tried to click the button you would see that it doesn't change the true either...

Upvotes: 0

Views: 209

Answers (2)

Roy J
Roy J

Reputation: 43881

Updating after a correcting comment from thanksd. I stumbled onto the right answer without really understanding it.

The problem is that within the slot, display refers to an item in the scope-slot object. Updating it there does not update the actual source variable. If you pass in and call a function, the proper variable is updated.

Vue.component('dropdown', {
  props: ['expanded'],
  data: function() {
    return {
      display: Boolean(this.expanded)
    }
  },
  methods: {
    toggle() {
        this.display = !this.display;
    }
  },
  template: '<div><transition name="expand"><slot :display="display" :toggle="toggle"></slot></transition></div>'
});

new Vue({
  el: '#app'
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
  <dropdown>
    <div slot-scope="{display, toggle}">
      <a href="javascript:void(0)" @click="toggle">Toggle {{ display }}</a>
      <div v-if="display">
        Dropdown content
      </div>
    </div>
  </dropdown>
</div>

Upvotes: 4

thanksd
thanksd

Reputation: 55644

One solution would be to implement a v-model for the dropdown component which would allow you to two-way bind the display property to a property in the parent. That way you wouldn't need to pass anything via the slot-scope.

Here's an example of that:

Vue.component('dropdown', {
  props: [ 'value' ],
  data() {
    return {
      display: !!(this.value)
    }
  },
  watch: {
    value(value) {
      this.$emit('input', value);
    }
  },
  template: '<div><transition name="expand"><slot></slot></transition></div>'
});

new Vue({
  el: '#app',
  data() {
    return { dropdownToggle: false }
  }
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
    <dropdown v-model="dropdownToggle">
      <div>
        <a href="javascript:void(0)" @click="dropdownToggle = !dropdownToggle">
          Toggle {{ dropdownToggle }}
        </a>
        <div v-if="dropdownToggle">
          Dropdown content
        </div>
      </div>
  </dropdown>
</div>

Upvotes: 1

Related Questions