如何从插槽子定义到vue 2.x中包含的组件进行通信?

时间:2017-06-07 15:43:32

标签: javascript html vuejs2

我正在编写一个简单的工具提示组件。工具提示的内容使用vue插槽定义。我希望插槽的内容能够发出工具提示的结束信号,就像工具提示可以从自己的模板中关闭一样。

以下是示例代码:



Vue.component('vtip', {
  created: function() {
    this.$on('closeDialog', function(a,b,c) {
      console.log('[closeDialog in vtip]', a,b,c,this);
      this.closeDialog(); // idfk
    });
  },
  props: ['button_text'],
  template: '#vtip_template',
  methods: {
    closeDialog: function(event) {
      var $dialog = $(this.$refs.dialog);
      $dialog.fadeToggle('fast');
    },
    toggleDialog: function(event) {
      var $button = $(this.$refs.button);
      var $dialog = $(this.$refs.dialog);

      var button_left = $button.position().left;
      var button_top = $button.position().top;

      var extras = 2;
      var arrow_size = 10;
      var new_pos = {left:button_left+'px',top:button_top+$button.height()+arrow_size+extras+'px'};

      $dialog.css(new_pos);
      $dialog.fadeToggle('fast');
    },
  }
});

var v_app;
$(document).ready(function () {
  v_app = new Vue({
    el: '#v_app',
  });
});

body {
  background:#eee;
  padding:1em;
  font-family:Verdana;
  font-size:11pt;
}

.dialog {
  background:#222;
  color:#eee;
  border:2px solid white;
  display:none;

  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
  border-radius:4px;

  position:absolute;
}

.dialog .item-body .item-body {
  padding:1em 1em;
}

.dialog .item-body {
  min-width:10em;
  padding:0.5em 1em;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>

<script id="vtip_template" type="x-template">
    <span style="position:relative;">
        <button @click="toggleDialog" v-text="button_text" ref="button"></button>
        <div ref="dialog" class="dialog">
            <div class='item-body'>
                <a @click="closeDialog" style="float:right;color:white;padding:1em;" href="#">x</a>
                <!-- i hate slots -->
                <slot>...</slot>
            </div>
        </div>
    </span>
</script>

<DIV id="v_app">

    1. Click Hey.<br>
    2. How do I make the "Yes!" button close the component like the "x" link does?<br>
    
    <vtip button_text="hey">
        <!-- how do i make it so this slot child can close the parent component -->
        Are you sure?
        <button>Yes!</button> <!-- @click="closeDialog" does not work (and should not) -->
    </vtip>   

</DIV>
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:2)

您可以向ref组件标记添加vtip属性,然后通过closeDialog对象引用组件的$refs方法:

<vtip button_text="hey" ref="tip">
    Are you sure?
    <button @click="$refs.tip.closeDialog()">Yes!</button>
</vtip>   

&#13;
&#13;
Vue.component('vtip', {
  created: function() {
    this.$on('closeDialog', function(a,b,c) {
      console.log('[closeDialog in vtip]', a,b,c,this);
      this.closeDialog(); // idfk
    });
  },
  props: ['button_text'],
  template: '#vtip_template',
  methods: {
    closeDialog: function(event) {
      var $dialog = $(this.$refs.dialog);
      $dialog.fadeToggle('fast');
    },
    toggleDialog: function(event) {
      var $button = $(this.$refs.button);
      var $dialog = $(this.$refs.dialog);

      var button_left = $button.position().left;
      var button_top = $button.position().top;

      var extras = 2;
      var arrow_size = 10;
      var new_pos = {left:button_left+'px',top:button_top+$button.height()+arrow_size+extras+'px'};

      $dialog.css(new_pos);
      $dialog.fadeToggle('fast');
    },
  }
});

var v_app;
$(document).ready(function () {
  v_app = new Vue({
    el: '#v_app',
  });
});
&#13;
body {
  background:#eee;
  padding:1em;
  font-family:Verdana;
  font-size:11pt;
}

.dialog {
  background:#222;
  color:#eee;
  border:2px solid white;
  display:none;

  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
  border-radius:4px;

  position:absolute;
}

.dialog .item-body .item-body {
  padding:1em 1em;
}

.dialog .item-body {
  min-width:10em;
  padding:0.5em 1em;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>

<script id="vtip_template" type="x-template">
    <span style="position:relative;">
        <button @click="toggleDialog" v-text="button_text" ref="button"></button>
        <div ref="dialog" class="dialog">
            <div class='item-body'>
                <a @click="closeDialog" style="float:right;color:white;padding:1em;" href="#">x</a>
                <!-- i hate slots -->
                <slot>...</slot>
            </div>
        </div>
    </span>
</script>

<DIV id="v_app">

    1. Click Hey.<br>
    2. How do I make the "Yes!" button close the component like the "x" link does?<br>
    
    <vtip button_text="hey" ref="tip">
        <!-- how do i make it so this slot child can close the parent component -->
        Are you sure?
        <button @click="$refs.tip.closeDialog()">Yes!</button> <!-- @click="closeDialog" does not work (and should not) -->
    </vtip>   

</DIV>
&#13;
&#13;
&#13;

或者,您也可以使用scoped slot,将closeDialog方法作为props中的点击处理程序属性传递。

在插槽定义中,绑定clickHandler属性:

<slot :clickHandler="closeDialog">...</slot>

然后,在您的父组件中,您可以通过范围clickHandler访问props,如下所示:

<vtip button_text="hey">
  <template scope="props">
    Are you sure?
    <button @click="props.clickHandler">Yes!</button> 
  </template>
</vtip>   

&#13;
&#13;
Vue.component('vtip', {
  created: function() {
    this.$on('closeDialog', function(a,b,c) {
      console.log('[closeDialog in vtip]', a,b,c,this);
      this.closeDialog(); // idfk
    });
  },
  props: ['button_text'],
  template: '#vtip_template',
  methods: {
    closeDialog: function(event) {
      var $dialog = $(this.$refs.dialog);
      $dialog.fadeToggle('fast');
    },
    toggleDialog: function(event) {
      var $button = $(this.$refs.button);
      var $dialog = $(this.$refs.dialog);

      var button_left = $button.position().left;
      var button_top = $button.position().top;

      var extras = 2;
      var arrow_size = 10;
      var new_pos = {left:button_left+'px',top:button_top+$button.height()+arrow_size+extras+'px'};

      $dialog.css(new_pos);
      $dialog.fadeToggle('fast');
    },
  }
});

var v_app;
$(document).ready(function () {
  v_app = new Vue({
    el: '#v_app',
  });
});
&#13;
body {
  background:#eee;
  padding:1em;
  font-family:Verdana;
  font-size:11pt;
}

.dialog {
  background:#222;
  color:#eee;
  border:2px solid white;
  display:none;

  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
  border-radius:4px;

  position:absolute;
}

.dialog .item-body .item-body {
  padding:1em 1em;
}

.dialog .item-body {
  min-width:10em;
  padding:0.5em 1em;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>

<script id="vtip_template" type="x-template">
    <span style="position:relative;">
        <button @click="toggleDialog" v-text="button_text" ref="button"></button>
        <div ref="dialog" class="dialog">
            <div class='item-body'>
                <a @click="closeDialog" style="float:right;color:white;padding:1em;" href="#">x</a>
                <!-- i hate slots -->
                <slot :clickHandler="closeDialog">...</slot>
            </div>
        </div>
    </span>
</script>

<DIV id="v_app">

    1. Click Hey.<br>
    2. How do I make the "Yes!" button close the component like the "x" link does?<br>
    
    <vtip button_text="hey">
      <template scope="props">
        Are you sure?
        <button @click="props.clickHandler">Yes!</button> 
      </template>
    </vtip>   

</DIV>
&#13;
&#13;
&#13;