3
votes

I am trying to only show an overlay, if my search input contains any text.

This is my template, where my input field is:

Input.vue:

<template>
    <div>
        <input v-model="query" class="input" placeholder="Global search..."></input>

    </div>
</template>
<script>
    export default {
        data() {
            return {
                query: '',
            };
        }
    };
</script>

When I check in my console, query updates to whatever text I write in my input field.

I then try to pass this variable to another component, which holds my overlay div:

Overlay.vue:

<template>
    <div v-if="this.$root.query.length > 0">
        <div class="search-overlay is-active"></div>
    </div>
</template>

However, this gives me below error:

[Vue warn]: Error in render: "TypeError: Cannot read property 'length' of undefined"

What am I doing wrong here?

2
Why would you need this.$root ? Could you provide more details about your different components ?Thomas Lombart
Give your query object as a prop to your overlay component : <overlay :query="query" /> and define the prop inside your Overlay.vue.Thoomas

2 Answers

2
votes

$root is the topmost component in the tree (the component you instantiated with new Vue()), which I don't believe is Input.vue.

In any case, if Input.vue were the root component, accessing the state of the component as you are is messy. If you want to share data across components, you should do so via props (data flows from parent to child), or for more complex cases you might need a shared data store (e.g. Vuex).

1
votes

You never should access to a component data like this. That's a bad way. You should take a look to VueX and state management pattern cause that a typical case that you have here.

However, if you don't want use VueX (or other tools for state management pattern), you should use event like this :

var Input = Vue.component('custom-input', {
  name : "custom-input",
  template : "#custom-input-template",
  props : ["value"],
  methods : {
    onInput(){    
      this.$emit('input', this.$refs.queryInput.value)
    }
  },
  created() {
    console.log("Custom-input created")
  }
});

var Overlay = Vue.component('custom-overlay', {
  name : "custom-overlay",
  template : "#custom-overlay-template",
  props : ["query"],
  created() {
    console.log("Custom-overlay created")
  }
});

new Vue({
    el: "#app",
    components : {
      Input,
      Overlay
    },
    data: {
		  query : null
    }
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
	<div>		
		 <custom-input v-model="query"></custom-input>
		 <custom-overlay v-show="query && query.length > 0" :query="query"></custom-overlay>
	</div>
</div>


<script type="text/x-template" id="custom-input-template">
    <div>
        <input :value="value" ref="queryInput" class="input" placeholder="Global search..." @input="onInput"></input>
    </div>
</script>

<script type="text/x-template" id="custom-overlay-template">
  <div>
	  {{query}}
  </div>
</script>