从孩子那里访问父母的$ refs - Vue.js

时间:2018-06-12 18:51:45

标签: javascript vue.js vuejs2

希望以前已经回答了这个问题 - 本质上我试图将一个块(“CodeBlock.vue”)附加到App.vue中的一个元素,这个元素来自在CodeBlock兄弟内部触发的onClick事件,以及App的子代。 vue,(“ButtonSidebar.vue”)。通过发出事件和/或使用eventBus Vue实例我有点困惑,所以任何指针都会非常感激:

到目前为止,我有以下内容。 CodeBlock.vue将用作实例并附加到App.vue中的div。

CodeBlock.vue:

<template>
  <div :class="type">
    THIS IS A CODE BLOCK!!
  </div>
</template>

<script>
export default {
  name: 'CodeBlock',
  props: [ 'type' ]
}
</script>

App.vue:

<template>
  <div id="app" class="container">
    <ButtonSidebar/>
    <div id="pageBlocks" ref="container"></div>
  </div>
</template>

<script>
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue'

// import { eventBus } from './main'

import AddTitle from './components/modules/AddTitle'
import AddSubTitle from './components/modules/AddSubTitle'
import ButtonSidebar from './components/modules/ButtonSidebar'
import CodeBlock from './components/modules/CodeBlock'

import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

Vue.use(BootstrapVue)

export default {
  name: 'App',
  components: {
    AddTitle,
    AddSubTitle,
    ButtonSidebar,
    CodeBlock
  }
}
</script>

<style>
#app {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #1f1f1f;
  margin-top: 60px;
}

.no-border {
  border: unset !important;
  border: 0px !important;
}
</style>

ButtonSidebar.vue:

<template>
  <div>
    <b-button class="btn-circle absolute-float-tight text-dark" v-on:click="reveal=!reveal">
      <font-awesome-icon v-if="!reveal" :icon="faPlusIcon" />
      <font-awesome-icon v-if="reveal" :icon="faMinusIcon" />
    </b-button>
    <transition name="custom-classes-transition" enter-active-class="animated bounceInDown" leave-active-class="animated bounceOutRight">
      <div v-if="reveal" class="absolute-float-reveal">
        <b-button class="btn-circle text-dark" v-on:click="addCodeBlock"><font-awesome-icon :icon="faCodeIcon" /></b-button>
      </div>
    </transition>
  </div>
</template>

<script>
import Vue from 'vue'

import FontAwesomeIcon from '@fortawesome/vue-fontawesome'
import faPlus from '@fortawesome/fontawesome-pro-regular/faPlus'
import faMinus from '@fortawesome/fontawesome-pro-regular/faMinus'
import faCode from '@fortawesome/fontawesome-pro-regular/faCode'

import CodeBlock from './CodeBlock'

export default {
  name: 'ButtonSidebar',
  computed: {
    faPlusIcon () {
      return faPlus
    },
    faMinusIcon () {
      return faMinus
    },
    faCodeIcon () {
      return faCode
    }
  },
  components: {
    FontAwesomeIcon,
    CodeBlock
  },
  data () {
    return {
      reveal: false
    }
  },
  props: ['codeBlocks'],
  methods: {
    addCodeBlock () {
      var ComponentClass = Vue.extend(CodeBlock)
      var instance = new ComponentClass({
        propsData: { type: 'primary' }
      })
      instance.$mount()

      this.$el.querySelector('#pageBlocks').appendChild(instance.$el)
    }
  }
}
</script>

<style scoped>

  .absolute-float-tight {
    left: 20px;
    position: absolute;
  }

  .absolute-float-reveal {
    left: 60px;
    position: absolute;
  }

  .btn-circle {
    background-color: transparent;
    border-radius: 50%;
    height: 34px;
    padding: 0;
    width: 34px;
  }

</style>

this.$el.querySelector('#pageBlocks').appendChild(instance.$el)部分,我开始略微放松一下......我担心我必须把所有东西都拆掉,然后重新开始?

2 个答案:

答案 0 :(得分:1)

我认为你可以用这种简单的方式实现它:

App.vue(模板部分)

<ButtonSidebar @add="addCodeItem"/>
<div id="pageBlocks">
    <codeBlock v-for="code in arrCodes" :type="code.type"/>
</div>

App.vue(脚本)

export default {
  data() {
     return {
        arrCodes: []
     }
  },
  methods: {
     addCodeItem(codeType) {
        this.arrCodes.push( { type: codeType } )
     }
  }
}

ButtonSidebar.vue(脚本部分)

addCodeBlock () {
  this.$emit('add', 'yourtype');
}

答案 1 :(得分:1)

您应该尽可能避免到达DOM。数据的真实来源应该在您的组件中。 refs对于集成需要DOM元素的其他js库非常有用。

因此,在您的情况下,假设codeBlocks组件中有App.vueSidebarButton需要在点击时发出事件,以便父App.vue可以添加新的Codeblock

(我删除了一些代码不需要的代码.CodeBlock.vue保持不变)

<强> App.vue

<template>
  <div id="app" class="container">
    <ButtonSidebar @add-block="addCodeBlock" />
    <CodeBlock v-for="block in codeBlocks" :type="block.type" />
  </div>
</template>

<script>
import ButtonSidebar from '../ButtonSidebar'
import CodeBlock from '../CodeBlock'

export default {
  name: 'App',

  components: {ButtonSidebar, CodeBlock},

  data() {
      return {
          codeBlocks: []
      }
  },
  methods: {
      addCodeBlock() {
          const newBlock = {type: 'whatever'}
          this.codeBlocks.push(newBlock)
      }
  }
}
</script>

<强> ButtonSideBar.vue

<template>
  <div>
    <b-button class="btn-circle text-dark" v-on:click="addCodeBlock</b-button>
  </div>
</template>

<script>  
export default {
  name: 'ButtonSidebar',

  data () {
    return {
      reveal: false
    }
  },

  methods: {
    addCodeBlock () {
      this.$emit('add-block')
    }
  }
}
</script>

在Vue中遵循的一个好模式是,当你想要在父母和孩子之间分享状态时,将状态提升为父母并将其作为道具传递下来。