简体   繁体   English

Vuex可重复使用的模块模式。使用函数使状态不起作用

[英]Vuex reusable module pattern. Using function for state not working

I have a form composed of 1 or more (user decides) Transactions. 我有一个由1个或多个(用户决定)交易组成的表格。 I display Transactions inside a component on the parent and set the Transaction's attributes with computed properties in the child (Transaction) component. 我在父组件的组件内显示事务,并在子(事务)组件中使用计算属性设置事务的属性。

User data is updated by the computed properties just fine however, when a user clicks to add an additional Transaction component the values from the first Transaction are duplicated for any new Transaction component/object created. 用户数据由计算属性更新,但是,当用户单击以添加其他事务组件时,第一个事务的值将与创建的任何新事务组件/对象重复。

I have read in the forum here and here that the solution was to use a function for state in the module definition. 我在论坛这里这里读过,解决方案是在模块定义中使用状态函数。 This doesn't appear to work for me, I'd like to learn why. 这对我来说似乎不起作用,我想了解原因。

Here is the declaration of the composite component Transaction: 这是复合组件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>

And here is the child component (computed props truncated for brevity, they are just the state attributes both getters and setters): 这里是子组件(为简洁而截断的计算道具,它们只是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>

Here is the vuex module for the child component (Transaction): 这是子组件的vuex模块(Transaction):

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;

Update: 更新:

I pass an object literal like so.. 我传递了一个像这样的对象文字。

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: '',
  });
},

I also tried wrapping my state in Transaction namespace, setting up a getter for this object and using it in the parent. 我还尝试在Transaction命名空间中包装我的状态,为该对象设置一个getter并在父对象中使用它。

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

FundTransaction's state: 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;
},

But this returns the duplicates as before. 但是这会像以前一样返回重复项。

Looking forward to your recommendation. 期待您的推荐。

In my experience, trying to use Vuex Modules as some sort of entity class doesn't work too well. 根据我的经验,尝试将Vuex模块用作某种实体类并不能很好地工作。 Since they're closely related, I'd recommend you move all of your transactions and fund records to a single static Vuex module. 由于它们密切相关,我建议您将所有交易和资金记录移至单个静态Vuex模块。 Although a transaction may be logically nested under a fund record, it'll be easier to keep your state flat and perform the nesting in a getter. 虽然交易可能在逻辑上嵌套在基金记录下,但更容易保持您的州持平并在吸气剂中执行嵌套。

I think that will simplify the relationship of your components and your store and either fix the issue or make its cause more obvious. 我认为这将简化组件与商店的关系,并解决问题或使其原因更加明显。

Here's a quick sketch of what that module might look like: 这是该模块可能的样子的快速草图:

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;

Here's what your computed properties might look like, assuming your FundTransaction component has a fund_transaction prop. 假设您的FundTransaction组件具有fund_transaction支柱,这就是您的计算属性的样子。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM