1

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.

1 Answer 1

1

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
Sign up to request clarification or add additional context in comments.

6 Comments

thanks. The problem I'm having is that in the App.vue file, I'm rendering the navbar and the router only, so I'm not sure if I can pass props via the router that are only meant for one of the routes? I want to pass the search query so as the user types, I can route from the 'dashboard' page to the 'searched movies' page with a list of their search results (which I will make a fetch for). Maybe I'm going about this totally wrong :/
Can I do this on every key press (so it's an incremental search) or will it just redirect and pass the props once, when the user submits?
@anna-collins, you know what, I think there's no need to redirect on every key press. You can redirect only once and pass the initial 'filter' string to the component, then, when you're on the right route (don't forget to prevent the router from redirecting in this case), you only update this string value — just like in the first example I provided. hope it'll make some sense.
Sorry, I forgot that you would be inside <router-view/> again. I this particular case you probably have to redirect every time. Or, alternatively you can emit an event on root component. Honestly, it'll be easier for you to just try a couple of options and figure out what works best. I'd say this is not very common situation you have.
Third option would be to store this string value somewhere globally, but this doesn't feel right. Also, keep in my that you are able to watch for route params inside your list component using: watch: { 'this.$route.params.<propname>'(value) { //do something on change }}
|

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.