javascript - 从子项访问父项的 $refs - Vue.js

标签 javascript vue.js vuejs2

希望之前已经回答过这个问题 - 本质上,我正在尝试将一个 block (“CodeBlock.vue”)附加到 App.vue 中的一个元素,该事件来自 CodeBlock 的同级和 App 的子级内部触发的 onClick 事件。 vue,(“ButtonSidebar.vue”)。我对发出事件和/或使用 eventBus Vue 实例感到有点困惑,因此非常感谢任何指针:

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

代码块.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) 部分,我开始有点松散情节......我很担心也许我必须剥离一切并重新开始?

最佳答案

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

所以在你的情况下,假设 codeBlocks 在你的 App.vue 组件中可用, SidebarButton 需要在被点击时发出一个事件以便父 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 中遵循的一个很好的模式是将状态提升给 parent ,并在您想要在 parent 和 child 之间共享状态时将其作为 Prop 传递下去。

关于javascript - 从子项访问父项的 $refs - Vue.js,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50823961/

相关文章:

vuejs2 - 具有 ServiceStack 路由回退和 Api 前缀的 Vue.js 路由器历史记录模式

javascript - 将变量传递给 handlebars helper 调用

php - 使用 javascript/php 的静态倒计时,怎么样?

javascript - 为什么 Angular 有 isDefined 和 isUndefined 函数?

javascript - 如何测试组件中另一个模块的自定义指令

vue.js - 在 Vue 中调试console.dir() react 变量的快速而肮脏的方法?

javascript - glyphicon 能够使用@click

javascript - 饼图中按类别求和(Dc.js 和 Crossfilter)

javascript - 根据响应在 vue 中重定向

javascript - Uncaught ReferenceError : process is not defined after upgrading from vue cli v4 to v5