首页 > 解决方案 > Vuex 可重用模块模式。使用状态功能不起作用

问题描述

我有一个由 1 个或多个(用户决定)事务组成的表单。我在父组件的组件内显示事务,并在子(事务)组件中使用计算属性设置事务的属性。

用户数据由计算属性更新,但是,当用户单击以添加额外的事务组件时,第一个事务的值将被复制用于创建的任何新事务组件/对象。

我在这里这里的论坛中读到,解决方案是在模块定义中使用状态函数。这似乎对我不起作用,我想了解原因。

下面是复合组件 Transaction 的声明:

<template v-for="(fund_transaction, index) in fund_transactions">
  <div class="card">
    <div class="card-body">
      <FundTransactionComponent
        v-bind:fund_transactions="fund_transactions"
        v-bind:key="index"
        v-on:removedTransaction="removeFundTransaction(id)"
        v-on:submittedTransaction="applyFundTransaction(fund_transaction.id)"
      >
      </FundTransactionComponent>
    </div><!--END .card-body-->
  </div><!--END .card-->
</template>

这是子组件(为简洁起见,计算的道具被截断,它们只是 getter 和 setter 的状态属性):

<template>
  <div class="row">
    <div class="col-10 float-left" v-if="this.fund_transactions.length > 0"></div>
    <div class="col-2 float-right" v-if="this.fund_transactions.length > 0">
      <button v-on:click="removeTransaction(index)" class="btn btn-icon btn-danger px-2 py-1 float-right">
        <i class="fa fa-times"></i>
      </button>
    </div><!--END .col-1 float-right-->
    <div class="col-md-6 col-sm-12">
      <label class="form-control-label text-semibold">Transaction Date:</label>
      <el-date-picker
        type="date"
        placeholder="select a date"
        v-model="date_of_record"
        style="width: 100%;"
        format="MMMM dd, yyyy"
        clearable
        default-date="Date.now()"
        >
      </el-date-picker>
    </div><!--END .col-md-6 .col-sm-12-->
    <div class="col-md-6 col-sm-12">
      <label class="form-control-label text-semibold">Reason for Transaction:</label>
      <textarea class="form-control" placeholder="Enter reason here.." v-model="reason_for_transaction">
      </textarea>
    </div><!--END .col-md-6 .col-sm-12-->
    <div class="col-md-6 col-sm-12">
      <label class="form-control-label text-semibold">Transaction Amount:</label>
      <input type="text" class="form-control" v-model="amount"/>
    </div><!--END .col-md-6 .col-sm-12-->
    <div class="col-md-6 col-sm-12">
      <label class="form-control-label text-semibold">Type of Transaction:</label><br>
      <el-radio-group
        v-model="transaction_type">
        <el-radio-button label="Deposit"></el-radio-button>
        <el-radio-button label="Withdrawal"></el-radio-button>
      </el-radio-group>
    </div><!--END .col-md-6 .col-sm-12-->
    <div class="col-md-6 col-sm-12">
      <label class="form-control-label text-semibold">Current Balance:</label>
      <input type="text" class="form-control" v-model="current_balance"/>
    </div><!--END .col-md-6 .col-sm-12-->
    <div class="col-md-6 col-sm-12">
      <label class="form-control-label text-semibold">Forwarded:</label>
      <input type="text" class="form-control" v-model="forwarded"/>
    </div><!--END .col-md-6 .col-sm-12-->
  </div><!--END .row-->
</template>
<script>
import moment from "moment";
import DatePicker from 'vuejs-datepicker';
import FundRecordForm2 from "@/store/modules/forms/FundRecordForm2";
import FundTransaction from "@/store/modules/auxillary/FundTransaction";
import Resident from "@/store/modules/actors/Resident";

export default {
  name: "FundTransaction",
  components: {
    DatePicker,
  },
  props: {
    index: {
      type: Number,
      required: false,
    },
  },
  computed: {
    id: {
      get() {
        return this.$store.getters['FundTransaction/getId'];
      },
      set(value) {
        this.$store.dispatch('FundTransaction/setId', value);
      },
    },
  },
  .
  .
  .
};
</script>
<style scoped>

</style>

这是子组件(事务)的 vuex 模块:

import Axios from "axios";
import router from "../../../router";
import FundRecordForm2 from "../forms/FundRecordForm2";

const FundTransaction = {
  namespaced: true,
  // Expectation: this should return individual object state respsectively
  // state: () => ({})
  state () {
    return {
      id: null,
      provider_id: Number,
      employee_id: Number,
      account_id: Number,
      resident_id: Number,
      fund_record_form2_id: Number,
      transaction_date: '',
      reason_for_transaction: '',
      transaction_type: '',
      amount: 0.0,
      current_balance: 0.0,
      forwarded: 0.0,
      date_of_record: '',
      created_at: '',
      updated_at: '',
    }
  },
  getters: {
    getId: (state) => {
      return state.id;
    },
    getProviderId: (state) => {
      return state.provider_id;
    },
    getEmployeeId: (state) => {
      return state.employee_id;
    },
    getAccountId: (state) => {
      return state.account_id;
    },
    getResidentId: (state) => {
      return state.resident_id;
    },
    getFundRecordForm2Id: (state) => {
      return state.fund_record_form2_id;
    },
    getTransactionDate: (state) => {
      return state.transaction_date;
    },
    getReasonForTransaction: (state) => {
      return state.reason_for_transaction;
    },
    getTransactionType: (state) => {
      return state.transaction_type;
    },
    getAmount: (state) => {
      return state.amount;
    },
    getCurrentBalance: (state) => {
      return state.current_balance;
    },
    getForwarded: (state) => {
      return state.forwarded;
    },
    getDateOfRecord: (state) => {
      return state.date_of_record;
    },
    getCreatedAt: (state) => {
      return state.created_at;
    },
    getUpdatedAt: (state) => {
      return state.updated_at;
    },
    getFundTransaction: (state) => {
      return state.fund_transaction;
    },
  },
  mutations: {
    SET_ID: (state, payload) => {
      state.id = payload;
    },
    SET_PROVIDER_ID: (state, payload) => {
      state.provider_id = payload;
    },
    SET_EMPLOYEE_ID: (state, payload) => {
      state.employee_id = payload;
    },
    SET_ACCOUNT_ID: (state, payload) => {
      state.account_id = payload;
    },
    SET_RESIDENT_ID: (state, payload) => {
      state.resident_id = payload;
    },
    SET_FUND_RECORD_FORM2_ID: (state, payload) => {
      state.fund_record_form2_id = payload;
    },
    SET_TRANSACTION_DATE: (state, payload) => {
      state.transaction_date = payload;
    },
    SET_REASON_FOR_TRANSACTION: (state, payload) => {
      state.reason_for_transaction = payload;
    },
    SET_TRANSACTION_TYPE: (state, payload) => {
      state.transaction_type = payload;
    },
    SET_AMOUNT: (state, payload) => {
      state.amount = payload;
    },
    SET_CURRENT_BALANCE: (state, payload) => {
      state.current_balance = payload;
    },
    SET_FORWARDED: (state, payload) => {
      state.forwarded = payload;
    },
    SET_DATE_OF_RECORD: (state, payload) => {
      state.date_of_record = payload;
    },
    SET_CREATED_AT: (state, payload) => {
      state.created_at = payload;
    },
    SET_UPDATED_AT: (state, payload) => {
      state.updated_at = payload;
    },
    UPDATE_FUND_TRANSACTION: (state, pyaload) => {
      state.fund_transaction = payload;
    },
  },
  actions: {
    setId (context, payload) {
      context.commit('SET_ID', payload);
    },
    setProviderId (context, payload) {
      context.commit('SET_PROVIDER_ID', payload);
    },
    setEmployeeId (context, payload) {
      context.commit('SET_EMPLOYEE_ID', payload);
    },
    setAccountId (context, payload) {
      context.commit('SET_ACCOUNT_ID', payload);
    },
    setResidentId (context, payload) {
      context.commit('SET_RESIDENT_ID', payload);
    },
    setFundRecordForm2Id (context, payload) {
      context.commit('SET_FUND_RECORD_FORM2_ID', payload);
    },
    setTransactionDate (context, payload) {
      context.commit('SET_TRANSACTION_DATE', payload);
    },
    setReasonForTransaction (context, payload) {
      context.commit('SET_REASON_FOR_TRANSACTION', payload);
    },
    setTransactionType (context, payload) {
      context.commit('SET_TRANSACTION_TYPE', payload);
    },
    setAmount (context, payload) {
      context.commit('SET_AMOUNT', payload);
    },
    setCurrentBalance (context, payload) {
      context.commit('SET_CURRENT_BALANCE', payload);
    },
    setForwarded (context, payload) {
      context.commit('SET_FORWARDED', payload);
    },
    setDateOfRecord (context, payload) {
      context.commit('SET_DATE_OF_RECORD', payload);
    },
    setCreatedAt (context, payload) {
      context.commit('SET_CREATED_AT', payload);
    },
    setUpdatedAt (context, payload) {
      context.commit('SET_UPDATED_AT', payload);
    },
    updateFundTransaction (context, payload) {
      context.commit('UPDATE_FUND_TRANSACTION', payload);
    },
  },
}
export default FundTransaction;

更新:

我像这样传递一个对象文字..

SET_NEW_FUND_TRANSACTION_FIELDS: (state) => {
  state.fund_transactions.push({
    id: null,
    provider_id: Number,
    employee_id: Number,
    account_id: Number,
    resident_id: Number,
    fund_record_form2_id: Number,
    transaction_date: '',
    reason_for_transaction: '',
    transaction_type: '',
    amount: 0.0,
    current_balance: 0.0,
    forwarded: 0.0,
    date_of_record: '',
    created_at: '',
    updated_at: '',
  });
},

我还尝试将我的状态包装在 Transaction 命名空间中,为此对象设置一个 getter 并在父对象中使用它。

SET_NEW_FUND_TRANSACTION_FIELDS: (state, getters, rootState, rootGetters) => {
  state.fund_transactions.push(rootGetters['FundTransaction/getFundTransaction']);
},

FundTransaction 的状态:

  state: () => ({
    fund_transaction: {
      id: null,
      provider_id: Number,
      employee_id: Number,
      account_id: Number,
      resident_id: Number,
      fund_record_form2_id: Number,
      transaction_date: '',
      reason_for_transaction: '',
      transaction_type: '',
      amount: 0.0,
      current_balance: 0.0,
      forwarded: 0.0,
      date_of_record: '',
      created_at: '',
      updated_at: '',
    }
  }),

getFundTransaction: (state) => {
  return state.fund_transaction;
},

但这会像以前一样返回重复项。

期待您的推荐。

标签: vue.jsvuexvuex-modules

解决方案


以我的经验,尝试将 Vuex 模块用作某种实体类并不能很好地工作。由于它们密切相关,我建议您将所有交易和资金记录移至单个静态 Vuex 模块。尽管事务可能在逻辑上嵌套在资金记录下,但保持状态平坦并在 getter 中执行嵌套会更容易。

我认为这将简化您的组件和商店之间的关系,并解决问题或使其原因更加明显。

以下是该模块的简要示意图:

const FundModule = {
  namespaced: true,
  state () {
    return {
      transactions: {
          // You'd probably make this an empty object, but it's 
          // an example of what the structure would look like.
          // You could us an array instead of an object, but I recommend 
          // keeping an object. It's easier to access items by key and 
          // Object.values() can quickly transform it to an array.
          1: {
            id: 1,
            provider_id: Number,
            employee_id: Number,
            account_id: Number,
            resident_id: Number,
            fund_record_form2_id: Number,
            transaction_date: '',
            reason_for_transaction: '',
            transaction_type: '',
            amount: 0.0,
            current_balance: 0.0,
            forwarded: 0.0,
            date_of_record: '',
            created_at: '',
            updated_at: '',
          }, 
          2: {
            id: 1,
            provider_id: Number,
            employee_id: Number,
            account_id: Number,
            resident_id: Number,
            fund_record_form2_id: Number,
            transaction_date: '',
            reason_for_transaction: '',
            transaction_type: '',
            amount: 0.0,
            current_balance: 0.0,
            forwarded: 0.0,
            date_of_record: '',
            created_at: '',
            updated_at: '',
          }
      },
      form_records: {
        /*Same idea here*/
      }
    }
  },
  getters: {
    // You can map your parent component to an array of transactions
    allTransactions: (state) => {
      return Object.values(state.transactions);
    },
    // If you only need transactions for a specific record or some 
    // other criteria you can perform that logic in a getter too.
    allTransactionsForFormRecord(state) => (form_record_id) => {
      return Object.values(state.transactions)
        .filter(t => t.form_record_form2_id === form_record_id);
    }
  },
  mutations: {
    SET_ID: (state, payload) => {
      state.transactions[payload.id].id = payload.value;
    },
    SET_PROVIDER_ID: (state, payload) => {
      state.transactions[payload.id].provider_id = payload.value;
    },
    SET_TRANSACTION_PROP: (state, payload) => {
      state.transactions[payload.id][payload.prop] = payload.value;
    },
    ADD_NEW_TRANSACTION: (state, payload) => {
      const id = generateNewId();
      state.transactions[id] = {
        id: id,
        provider_id: 0,
        employee_id: 0,
        account_id: 0,
        resident_id: 0,
        fund_record_form2_id: 0,
        transaction_date: '',
        reason_for_transaction: '',
        transaction_type: '',
        amount: 0.0,
        current_balance: 0.0,
        forwarded: 0.0,
        date_of_record: '',
        created_at: '',
        updated_at: '',
      };
    }
  },
  actions: {
    setId (context, payload) {
      context.commit('SET_ID', payload);
    },
    setProviderId (context, payload) {
      context.commit('SET_PROVIDER_ID', payload);
    },
    // If you want a generic way to set any property
    setTransactionProp (context, payload) {
      context.commit('SET_TRANSACTION_PROP', payload);
    },
    addNewTransaction (context, payload) {
      context.commit('ADD_NEW_TRANSACTION', payload);
    }
  },
}
export default FundModule;

fund_transaction假设您的 FundTransaction 组件有一个道具,这就是您的计算属性可能的样子。

computed: {
  id: {
    get() {
      return this.fund_transaction.id;
    },
    set(value) {
      this.$store.dispatch('FundModule/setId', {value, id: this.fund_transaction.id});
    },
  },
},

推荐阅读