oliverbj
oliverbj

Reputation: 6062

Error in render: "TypeError: Cannot read property 'length' of undefined"

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?

Upvotes: 3

Views: 4084

Answers (2)

Ekushisu
Ekushisu

Reputation: 461

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>

Upvotes: 1

Decade Moon
Decade Moon

Reputation: 34306

$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).

Upvotes: 2

Related Questions