0

I am trying to pass data from the parent component(Home route) to the child component(playlist route) via props. In the child component I get the error "TypeError: Cannot read property 'length' of undefined". The two components are on the same page at the moment but I am trying to move the child component to its own route which is causing this error.

App.vue

<template>
  <div id="app">
    <Header></Header>
    <router-view></router-view>
    <Footer></Footer>
  </div>
</template>

<script>
import Header from './components/header.vue'
import Footer from './components/footer.vue'


export default {
  name: 'app',
  components: {
    Header,
    Footer,
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 0px;
}
</style>

Main.js

import Vue from 'vue';
import App from './App.vue';
import "jquery";
import "bootstrap";
import VueRouter from 'vue-router';

import "bootstrap/dist/css/bootstrap.min.css"
import { library } from '@fortawesome/fontawesome-svg-core'
import { faSearch } from '@fortawesome/free-solid-svg-icons'
import { faRedo } from '@fortawesome/free-solid-svg-icons'
import { faPlus } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import Home from './components/home.vue'
import Next from './components/next.vue'
import List from './components/myList.vue'


library.add(faSearch)
library.add(faRedo)
library.add(faPlus)

Vue.component('font-awesome-icon', FontAwesomeIcon)
Vue.use(VueRouter);
Vue.config.productionTip = false

const router = new VueRouter({
  routes: [
    {
      path: '/home',
      component: Home
    },
    {
      path: '/next',
      component: Next
    },
    {
      path: '/playlist',
      component: List
    }
  ]
});

new Vue({
  render: h => h(App),
  router
}).$mount('#app')

Parent component:

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

    <div class="jumbotron" style="clear:both">
      <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>-->

              <button
                type="button"
                class="btn btn-primary btn-lg mb-3 float-right"
                v-on:click="addItem(result)"
                :disabled="result.disableButton"
              >

                <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",
  components: {
    List
  },
  data: function() {
    return {
      title: "Simple Search",
      isActive: true,
      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: [],
      List: [],
      search: "",
      loading: "",
      message: false,
      isValidationAllowed: false,
      loadingClass: "loading",
      errorTextClass: "error-text",
      disableButton: false
    };
  },

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

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

  methods: {
    getData: function() {
      this.isValidationAllowed = true;
      this.loading = true;
      fetch(`https://thisapi.com/api`)
        .then(response => response.json())
        .then(data => {
          this.result = data.results;
          this.loading = false;
          /* eslint-disable no-console */
          console.log(data);
          /* eslint-disable no-console */
        });
    },

    toggleClass: function() {
      // Check value
      if (this.isActive) {
        this.isActive = false;
      } else {
        this.isActive = true;
      }
    },

    refreshPage: function() {
      this.search = "";
    },
    addItem: function(result) {
      result.disableButton = true; // Or result['disableButton'] = true;
      this.List.push(result);
      /* eslint-disable no-console */
      console.log(result);
      /* eslint-disable no-console */
    },

    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;
}

.red {
  background: red;
}

.blue {
  background: blue;
}

.div {
  width: 100px;
  height: 100px;
  display: inline-block;
  border: 1px solid black;
}
</style>

Child component or /list (route)

<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">{{List.length}}</span>
    </button>

    <div class="col-md-4 float-left p-2 mb-3 " v-for="(result, index) in List" :key="index">
      <img
          :src="resizeArtworkUrl(result)"
          alt="Album Cover"
          class="album-cover align-self-start mr-3 card-img-top"
        >
      <div class="card-body">
        <h5 class="card-title">{{result.collectionName}}</h5>
        <h6 class="mt-0 mb-2">{{result.artistName}}</h6>
        <p class="mt-0 mb-2">{{result.primaryGenreName}}</p>

        <button class="btn btn-danger" v-on:click="removeElement(result)">Remove</button>
      </div>
    </div>
  </div>
</template>

<script>
export default {
   name: 'List',
   props: 
   ["List"],

  methods: {
    removeElement: function(index) {
      this.List.splice(index, 1);
    },

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

<style scoped>

.album-cover {
    width: 100%;
    height: auto;
    background-color: aqua;
}

</style>
2
  • Where are you passing the list parameter to the list component.I don't see that so the length of the prop would be definitely undefined. Commented Jun 24, 2019 at 5:06
  • It is in the parent component as List: [],. Where should I pass that? Commented Jun 24, 2019 at 8:16

1 Answer 1

1

If you want to redirect from the current parent to your playlist component you could do this:

this.$router.push({
    path: '/playlist',
    params: {List: this.List}
});

Then in your router add the props property:

{
    path: '/playlist',
    component: List,
    props: true,
}
Sign up to request clarification or add additional context in comments.

3 Comments

Can you elaborate where this would need to be added, as currently it breaks everything and does not show no errors
@Sole The first part in any methods or watcher of the parent component
Thanks, but doing that just redirects to the playlist route and even then there is the same length' of undefined

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.