Anna Collins
Anna Collins

Reputation: 33

Fetch data in component on initiation using parameters from Vuex store

I am new to Vue and am trying to build a simple movie app, fetching data from an API and rendering the results. I want to have an incremental search feature. I have an input field in my navbar and when the user types, I want to redirect from the dashboard view to the search results view. I am unsure of how to pass the query params from the navbar to the search results view.

Here is my App.vue component

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

<script>
import Navbar from './components/Navbar.vue'
export default {
  name: 'App',
  components: {
    Navbar
  },
}
</script>

And here is my navbar component where I have the input field

<template>
  <nav class="navbar">
    <h1 class="logo" v-on:click="goToHome">Movie App</h1>
    <input class="search-input" v-on:keyup="showResults" v-model="query" type="text" placeholder="Search..."/>
  </nav>
</template>

<script>
import router from '../router/index'
export default {
  data: function () {
    return {
      query: this.query
    }
  },
  methods: {
    goToHome () {
      router.push({name: 'Dashboard'})
    },
    showResults () {
      //here on each key press I want to narrow my results in the SearchedMovies component
    }
  }
}
</script>

If I use router.push to the SearchedMovies component then I am only able to pass the query as a parameter once. I thought about using Vuex to store the query and then access it from the SearchedMovies component, but surely there is a better way of doing it?

I also read about using $emit but since my parent contains all the routes, I'm not sure how to go about this.

Upvotes: 1

Views: 1115

Answers (1)

oniondomes
oniondomes

Reputation: 2063

You don't need to redirect user anywhere. I've made a small demo to show how one might do it. I used this navbar component as you described and emit an event from it:

const movies = {
  data: [
    {
      id: 0,
      title: 'Eraserhead',
    },
    {
      id: 1,
      title: 'Erazerhead',
    },
    {
      id: 2,
      title: 'Videodrome',
    },
    {
      id: 3,
      title: 'Videobrome',
    },
    {
      id: 4,
      title: 'Cube',
    },
  ]
};

Vue.component('navbar', {
  template: '<input v-model="filter" @input="onInput" placeholder="search">',
  data() {
    return {
      filter: '',
    };
  },
  methods: {
    onInput() {
      this.$emit('filter', this.filter);
    }
  }
});

// this is just a request imitation. 
// just waiting for a second until we get a response
// from the datasample
function request(title) {
  return new Promise((fulfill) => {
    toReturn = movies.data.filter(movie => movie.title.toLowerCase().indexOf(title.toLowerCase()) !== -1)
    setTimeout(() => fulfill(toReturn), 1000);
  });
}


new Vue({
  el: '#app',
  data: {
    movies: undefined,
    loading: false,
    filter: '',
    lastValue: '',
  },
  methods: {
    filterList(payload) {
      // a timeout to prevent 
      // instant request on every input interaction
      this.lastValue = payload;
      setTimeout(() => this.makeRequest(), 1000);
    },
    makeRequest() {
      if (this.loading) {
        return;
      }
      this.loading = true;
      request(this.lastValue).then((response) => {
        this.movies = response;
        this.loading = false;
      });
    }
  },
  mounted() {
    this.makeRequest('');
  }
})
<script src="https://unpkg.com/vue"></script>

<div id="app">
  <navbar v-on:filter="filterList"></navbar>
  <ul v-if="!loading">
    <li v-for="movie in movies" :key="movie.id">{{ movie.title }}</li>
  </ul>
  <p v-else>Loading...</p>
</div>

Also jsfiddle: https://jsfiddle.net/oniondomes/rsyys3rp/

If you have any problem to understand the code above let me know.

EDIT: Fixed some bugs and added a couple of comments

EDIT2(after the comment below):

Here's what you can do. Every time user inputs something inside a navbar you call a function:

// template
<navbar v-on:input-inside-nav-bar="atInputInsideNavBar"></navbar>

// script
methods: {
    atInputInsideNavBar(userInput) {
        this.$router.push({
            path: '/filtred-items',
            params: {
                value: userInput
            }
        })
    }
}

Then inside you 'searched movies' page component you can access this value so:

this.$route.params.value // returns userInput from root component

Upvotes: 1

Related Questions