Search code examples
javascriptvue.jsvuejs2vue-componentvuex

How can I solve Computed property "search" was assigned to but it has no setter?


I use vue js version 2

My child component like this :

<template>
    ...
        <div class="search-sm">
            <b-input placeholder="Search"  @input="(val) => searchChange(val)" />
        </div>
    ...
</template>
<script>

export default {
  props: [
    "searchChange",
  ]
};
</script>

When search, it will call method in parent component

The parent component like this :

<template>
  ...
      <page-heading
        :searchChange="searchChange"
      ></page-heading>
  ...
</template>

<script>
import { mapActions, mapGetters } from "vuex";
import PageHeading from "@/views/app/reports/PageHeading";

export default {
  components: {
    "page-heading": PageHeading,
  },
  methods: {
    ...mapActions(["getReports"]),
    searchChange(val) {
      this.search = val;
      this.page = 1;
    },
    changePage(pageNum) {
      this.page = pageNum;
    },
  },
  computed: {
    ...mapGetters({
      page: "reportPage",
      search: "reportSearch",
    }),
  },
  watch: {
    search() {
      this.page = 1;
    },
  },
  mounted() {
    this.getReports();
  }
};
</script>

I use vuex store. It's like this :

import axios from 'axios'

const state = {
  page: 1,
  search: "",
}

const getters = {
  reportSearch: state => state.search,
  reportPage: state => state.page,
}

const mutations = {
  getReportSuccess (state, res) {
    state.total = res.data.length;
    state.perPage = res.perPage;
  },
}

const actions = {
  getReports ({ commit }) {
    axios
      ...
  }
}

export default {
  state,
  getters,
  mutations,
  actions
}

When I try to search there exist error like this :

[Vue warn]: Computed property "page" was assigned to but it has no setter.

How can I solve this problem?

Please help. Thank you very much


Solution

  • I see two things

    • a mapped getter for page and search
        ...mapGetters({
          page: "reportPage",
          search: "reportSearch",
        }),
      
    • and a mutation of the getters
        searchChange(val) {
          this.search = val;
          this.page = 1;
        },
      

    vuex getters don't have setters, you have to invoke a mutation manually

    to get around that, you need to invoke the vuex action to update the state

    const mutations = {
      getReportSuccess (state, res) {
        state.total = res.data.length;
        state.perPage = res.perPage;
      },
      setReportPage(state, page) {
        state.reportPage = page;
      },
      setReportSearch(state, search) {
        state.reportSearch = search;
      },
    }
    
    const actions = {
      getReports ({ commit }) {
        axios
          ...
      },
      setPage({commit}, page) {
        commit('setReportPage', page);
      },
      setSearch({commit}, search) {
        commit('setReportSearch', search);
      },
    }
    

    this will add the actions and mutations needed to update individually

    then instead of these methods:

      methods: {
        ...mapActions(["getReports"]),
        searchChange(val) {
          this.search = val;
          this.page = 1;
        },
        changePage(pageNum) {
          this.page = pageNum;
        },
      },
    

    you would have

      methods: {
        ...mapActions(["getReports", "setPage", "setSearch"]),
        searchChange(val) {
          this.setSearch(val);
          this.setPage(1);
        },
        changePage(pageNum) {
          this.setPage(pageNum);
        },
      },
    

    and the watch can be

      watch: {
        search() {
          this.setPage(1);
        },
      },
    

    alternatively...

    you could use a computed with a set and get like

      computed: {
        ...mapGetters("reportPage","reportSearch"]),
        page: 
          get: function () {
            return this.reportPage;
          },
          set: function (value) {
            this.setPage(value)
          },
        search: 
          // or if you don't map getters and actions ...
          get: function () {
            return this.$store.getter.reportSearch;
          },
          set: function (value) {
            this.$store.commit("setSearch", value);
            this.$store.commit("setPage", 1); // optional if you want to remove use of `searchChange`
          },
      },
    

    this approach would allow you to map the v-model

    <b-input placeholder="Search" v-model="search" />