I have a demo vue app, which renders some weather data for some cities. I am using Vue hash router for 3 routes:

  1. / (home page)
  2. /weather/:id (for detailed forecast for specific location)
  3. /search/:term (for searching locations)

All work fine except the /search/:term route which needs to re-update when search term changes and also when navigating back to previous search page with browser's back button.

Search page looks like (url: http://localhost/#/search/lon):

enter image description here

Search page component:

<template>
  <div :key="'search_page_'+$route.params.term" class="page page-search">
    <h2 class="page-header">Search Results</h2>
    <div class="search-box">
        <input type="text" placeholder="search.." v-model="text" />
        <button type="button" @click="handleOnClick">Search!</button>
    </div>
    <Loader v-if="loading" text="Loading.." />
    <template v-else-if="data && data.length">
    <Weather v-for="place in data" :key="'weather_for_'+place.woeid" :woeid="String(place.woeid)" :city="place.title" />
    </template>
    <p v-else><b>No results found!</b></p>
</div>
</template>

<script>
// @ is an alias to /src
import Loader from '@/components/Loader.vue'
import Weather from '@/components/Weather.vue'
import store from '@/modules/Store'

const API_URL = '/weather.php?keyword=';

export default {
  name: 'search',
  components: {
    Loader, Weather
  },
  data: function() {
    return {
        loading: true,
        text: this.$route.params.term ? decodeURIComponent(this.$route.params.term) : '',
        data: null
    };
  },
  methods: {
    handleOnClick: function() {
        this.$router.push({ name: 'search', params: { term: encodeURIComponent(this.text) } });
    },
    loadData: function( withText ) {
        let searchData = store.get('search_'+this.$route.params.term);
        if ( searchData ) {
            if ( withText ) this.text = decodeURIComponent(this.$route.params.term);
            this.data = searchData;
            this.loading =  false;
        } else {
            fetch(API_URL+this.$route.params.term)
            .then(response => response.json())
            .then(jsonResponse => {
                store.set('search_'+this.$route.params.term, searchData = jsonResponse);
                if ( withText ) this.text = decodeURIComponent(this.$route.params.term);
                this.data = searchData;
                this.loading =  false;
            })
            .catch(error => console.log(error));
        }
    }
  },
  watch: {
    '$route': function(to, from) {
        if ( from.params.term !== to.params.term )
        {
            this.loadData(true);
        }
    }
  },
  mounted: function() {
    this.loadData();
  }/*,
  updated: function() {
    this.loadData();
  }*/
}
</script>

Router configuration:

import Vue from 'vue'
import Router from 'vue-router'
import HomePage from './views/HomePage.vue'
import SearchPage from './views/SearchPage.vue'
import WeatherPage from './views/WeatherPage.vue'

Vue.use(Router)

export default new Router({
  mode: 'hash',
  base: '/',
  routes: [
    {
      path: '/',
      name: 'home',
      component: HomePage
    },
    {
      path: '/search/:term',
      name: 'search',
      component: SearchPage
    },
    {
      path: '/weather/:woeid',
      name: 'weather',
      component: WeatherPage
    }
  ]
})

I have made it work (although search page is a little flickering when updating search term on same page and not as reactive as I would expect) but I am wondering if this is the best approach I can take or is there a better one?

For example I use lifecycle hooks to fetch data from API, and also use watchers on route to be able to react when search term changes on same page. Also have added :key property on whole component. But I have the impression this can be done in a simpler/better way. maybe I am missing sth and/or maybe I am doing things twice (for example I am not sure if loadData is called twice when first entering search page, since I both loadData on mounted hook and on $route change).

UPDATE to partly answer 2nd part of the question: loadData method is not called twice on first entering search page. Either the mounted lifecycle hook is called when first entering the page, or the $route watcher is triggered when updating search page from inside the page itself (including browser's back button), but not both.

0 Answers