0

I have a component which is trying to push data to another sub component, Abit confused here as I am new to Vue.js. I have tried to import the subcomponent into the main one, but the push for the add item is causing the issue? Any ideas?

I get the error:

TypeError: Cannot read property 'push' of undefined

I cant see why or where?

Main component:

<template>
  <div class="container search">
    <list></list>

    <div class="jumbotron">
      <h1 class="display-4">{{title}}</h1>
      <p class="lead">{{intro}}</p>
      <hr class="my-4">
      <p v-if="validated" :class="errorTextClass">Enter a valid search term</p>

      <button
        type="button"
        class="btn btn-primary btn-lg mb-3"
        v-on:click="refreshPage"
        v-if="result.length > 1"
      >
        <font-awesome-icon icon="redo"/>Start again
      </button>
      <input
        class="form-control form-control-lg mb-3"
        type="search"
        placeholder="Search"
        aria-label="Search"
        v-model="search"
        required
        autocomplete="off"
        id="search"
      >

      <div v-for="(result, index) in result" :key="index">
        <div class="media mb-4">
          <img
            :src="resizeArtworkUrl(result)"
            alt="Album Cover"
            class="album-cover align-self-start mr-3"
          >
          <div class="media-body">
            <h4 class="mt-0">
              <button
                type="button"
                class="btn btn-primary btn-lg mb-3 float-right"
                v-on:click="addItem(result)"
              >
                <font-awesome-icon icon="plus"/>
              </button>
              <b>{{result.collectionName}}</b>
            </h4>
            <h6 class="mt-0">{{result.artistName}}</h6>
            <p class="mt-0">{{result.primaryGenreName}}</p>
          </div>
        </div>
      </div>

      <div :class="loadingClass" v-if="loading"></div>

      <button
        class="btn btn-success btn-lg btn-block mb-3"
        type="submit"
        v-on:click="getData"
        v-if="result.length < 1"
      >
        <font-awesome-icon icon="search"/>Search
      </button>
    </div>
  </div>
</template>

<script>
import List from "../components/myList.vue";

export default {
  name: "Hero",
  props: {
    List
  },
  components: {
    List
  },
  data: function() {
    return {
      title: "Simple Search",
      intro: "This is a simple hero unit, a simple jumbotron-style.",
      subintro:
        "It uses utility classes for typography and spacing to space content out.",
      result: [],
      errors: [],
      search: "",
      loading: "",
      message: false,
      isValidationAllowed: false,
      loadingClass: "loading",
      errorTextClass: "error-text"
    };
  },

  watch: {
    search: function(val) {
      if (!val) {
        this.result = [];
      }
    }
  },

  computed: {
    validated() {
      return this.isValidationAllowed && !this.search;
    }
  },

  methods: {
    getData: function() {
      this.isValidationAllowed = true;
      this.loading = true;
      fetch(`xxxxxxxx`)
        .then(response => response.json())
        .then(data => {
          this.result = data.results;
          this.loading = false;
          /* eslint-disable no-console */
          console.log(data);
          /* eslint-disable no-console */
        });
    },
    refreshPage: function() {
      this.search = "";
    },
    addItem: function(result) {
      this.myList.push(result);
    },

    resizeArtworkUrl(result) {
      return result.artworkUrl100.replace("100x100", "160x160");
    }
  }
};
</script>

<style>
.loading {
  background-image: url("../assets/Rolling-1s-42px.gif");
  background-repeat: no-repeat;
  height: 50px;
  width: 50px;
  margin: 15px;
  margin-left: auto;
  margin-right: auto;
}

.error-text {
  color: red;
}

.media {
  text-align: left;
}

.album-cover {
  width: 80px;
  height: auto;
}
</style>

Sub component:

<template>
  <div class="mb-5 container">
    <button type="button" class="btn btn-primary mt-2 mb-2 btn-block">
      My List
      <span class="badge badge-light">{{myList.length}}</span>
    </button>
    <ul class="list-group" v-for="(result, index) in myList" :key="index">
      <li class="list-group-item">
        <b>{{result.collectionName}}</b>
        <h6 class="mt-0">{{result.artistName}}</h6>
        <p class="mt-0">{{result.primaryGenreName}}</p>
      </li>
    </ul>
  </div>
</template>

<script>

export default {
  name: "List",

  data: function() {
    return {
      myList: [],
      result: []
    };
  }
}
</script>
3
  • The myList property does not exist on the Main component. You probably want to define it as a data property on the Main component, then pass it down to the Sub component as a prop. Commented Jun 18, 2019 at 23:09
  • How would I pass it down to the sub component as a prop? Commented Jun 18, 2019 at 23:15
  • Tried that but i get TypeError: Cannot read property 'length' of undefined Commented Jun 18, 2019 at 23:20

1 Answer 1

1

Data should always flow from parent to child components; child components shouldn't be modifying the data belonging to parent components.

If you want the main component to be able to push an item to myList, then myList should belong to the main component and you pass it to the sub component via a prop.

Simplified example:

Main component

<list :list="list"/>
<button @click="addItem">Add Item</button>
import List from './list.vue'

export default {
  components: {
    List
  },
  data() {
    return {
      list: []
    }
  },
  methods: {
    addItem() {
      this.list.push('New Item')
    }
  }
}

List component

<ul>
  <li v-for="item of list">{{ item }}</li>
</ul>
export default {
  props: ['list']
}
Sign up to request clarification or add additional context in comments.

5 Comments

Why is the v-for="item of items" where is items defined?
the above gives an error error: Elements in iteration expect to have 'v-bind:key' directives
Sorry that was a typo.
error: 'item' is defined but never used Where is item defined for use?
Regarding the key warning, that isn’t required but is enforced by eslint. You should review v-for documentation for more information.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.