When I click on a breadcrumb, it is not displaying what was just previously being displayed there…why is this?

vue-router
vuex

#1

I’m working on a property search for an app.

After searching for a property, a table will be displayed of property search results, and the breadcrumbs at the top of the page will look like this: Dasboard / Property Search .

On the table, you can click on a loan number and go to the loan detail page, which will now make the breadcrumbs look like this: Dashboard / Property Search / Loan Detail .

Now, when I try to go back by clicking on the Property Search breadcrumb, it won’t display anything. Just an empty component.

I want the property search results component to still be displayed after clicking on the loan detail page and then clicking on the breadcrumb to go back to the results component, but I can’t figure out how to do that.

I tried using the component, but I couldn’t get that to work. I was thinking I needed something like a singleton, but I wasn’t able to get that working. I also tried messing around with the router, but couldn’t figure that out either.

Property Search component:

<template>
  <form @submit.prevent="submitSearch">
    <div class="search-bar-pad white">
      <b-input-group>
        <b-form-input v-model="searchCriteria" size="sm" type="text"></b-form-input>
        <b-input-group-append>
          <b-btn type="submit" size="sm" variant="danger">
            <i class="fa fa-search" aria-hidden="true"></i>
          </b-btn>
        </b-input-group-append>
      </b-input-group>
    </div>
  </form>
</template>




<script>
export default {
  name: "PropertySearch",
  data() {
    return {
      searchCriteria: ""
    };
  },
  methods: {
    submitSearch() {
      let search = {
        search: this.searchCriteria
      };
      this.$store.dispatch("toggleLoading", true);
      this.$store.dispatch("searchProperties", search);
      this.$router.push("/PropertySearch");
    }
  }
};
</script>

PropertySearchResults component:

<template>
  <div class="animated fadeIn">
    <b-card class="min-height-8" template slot="header">
      <div slot="header">Property Search Results</div>
      <b-container fluid class="px-0 py-0">
        <div v-if="results.length > 0" class="recordBar animated fadeIn">
          <div class="counter float-right">
            <span class="val">{{ results.length || 0 }}</span> Record(s)
          </div>
        </div>
        <loading-spinner :show="loading"></loading-spinner>
        <div v-if="loading == false && results.length == 0">
          <b-alert show class="text-center">No Records Found</b-alert>
        </div>
        <b-table
          v-if="results.length > 0"
          table
          striped
          bordered
          hover
          class="animated fadeIn"
          :items="results"
          :fields="fields"
          :currentPage="currentPage"
          :per-page="perPage"
        >
          <template slot="loanNumber" slot-scope="data">
            <router-link
              :to="{ name: 'Loan', params: { loanId: String(data.item.loanId)}}"
            >{{data.item.loanNumber}}</router-link>
          </template>
        </b-table>
        <b-pagination
          v-if="results.length > 10"
          limit="10"
          class="mt-5"
          :total-rows="results.length"
          :per-page="perPage"
          v-model="currentPage"
        />
      </b-container>
    </b-card>
  </div>
</template>



<script>
import { mapState } from "vuex";

export default {
  data() {
    return {
      results: [],
      loading: true,
      currentPage: 1,
      perPage: 15,
      fields: [
        { key: "loanId", label: "Loan ID", sortable: "true" },
        { key: "workorderNumber", label: "WO Number", sortable: "true" },
        { key: "dateOrdered", label: "Date Ordered", sortable: "true" },
        { key: "loanNumber", label: "Loan Number", sortable: "true" },
        { key: "loanType", label: "Loan Type", sortable: "true" },
        { key: "address1", label: "Street", sortable: "true" },
        // { key: "address2", label: "Address 2", sortable: "true" },
        { key: "city", label: "City", sortable: "true" },
        { key: "state", label: "State", sortable: "true" },
        { key: "zip", label: "Zip", sortable: "true" },
        { key: "clientCode", label: "Client Code", sortable: "true" },
        { key: "clientName", label: "Client Name", sortable: "true" }
      ]
    };
  },
  created() {
    this.$store.watch(
      state => {
        return this.$store.state.currentlyLoading;
      },
      (newVal, oldVal) => {
        this.loading = newVal;
      }
    );

    this.$store.watch(
      state => {
        return this.$store.state.searchResults;
      },
      (newVal, oldVal) => {
        this.results = newVal;
      }
    );
  }
};
</script>

Section of router:

 routes: [{
      path: '/',
      redirect: '/dashboard',
      name: 'Dashboard',
      component: DefaultContainer,
      children: [{
          path: '/dashboard',
          name: '', 
          component: Dashboard,
          meta: {
            requiresAuth: true
          }
        },
        {
          path: "/PropertySearch",
          name: "Property Search",
          component: Container,
          children: [{
              path: '/',
              name: '',
              component: PropertySearchResults
            },
            {
              path: '/LoanDetail/:loanId',
              name: 'Loan',
              component: LoanDetail
            }
          ]
        }

Store:

import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'

Vue.use(Vuex, axios)


export const store = new Vuex.Store({
  state: {
    searchResults: [],
    currentlyLoading: false
  },
  mutations: {
    UPDATE_SEARCH_RESULTS(state, payload) {
      state.searchResults = payload;
    },
    TOGGLE_LOADING(state, payload) {
      state.currentlyLoading = payload;
    }
  },
  actions: {
    searchProperties({
      commit
    }, searchValue) {
      axios
        .post("/search/property", searchValue)
        .then(function (res) {
          commit('UPDATE_SEARCH_RESULTS', res.data);
          commit('TOGGLE_LOADING', false)
        })
        .catch(function (err) {
          commit('TOGGLE_LOADING', false)
          console.log(err);
        })
    },
    toggleLoading({
      commit
    }, bool) {
      commit('TOGGLE_LOADING', bool);
    }
  }
})

#2

I believe this is your issue:

this.$store.watch(
  state => {
    return this.$store.state.searchResults;
  },
  (newVal, oldVal) => {
    this.results = newVal;
  }
);

on creation you setup a watcher, however a watcher is only effective when the value of what’s being watched changes. As your searchResults haven’t changed, then the results are not populated.

I’m not sure why you set the Vuex state to a local component state, but it’s best (and simpler) to just use the Vuex state. A simple getter is all you need, but you can map the state to a computed prop in your component if you like.

This way your component will always reflect the current state of your Vuex model.


#3

Hi James, thanks for your reply!

I changed it to this:

 computed: {
    ...mapGetters(["results", "loading"])
  }

and I deleted the watcher. Although that did work, it still gives me the same problem, where if I try to go back and click on the property search breadcrumb, it will not show me the PropertyResultsComponent anymore.


#4

Is the search results state still populated in Vue DevTools?