Vuex 可重用模块模式。使用状态功能不起作用
Posted
技术标签:
【中文标题】Vuex 可重用模块模式。使用状态功能不起作用【英文标题】:Vuex reusable module pattern. Using function for state not working 【发布时间】:2019-10-19 08:00:30 【问题描述】:我有一个由 1 个或多个(用户决定)事务组成的表单。我在父组件的组件内显示事务,并在子(事务)组件中使用计算属性设置事务的属性。
用户数据通过计算属性更新就好了,但是,当用户单击以添加额外的事务组件时,第一个事务的值被复制用于创建的任何新事务组件/对象。
我在论坛here 和here 中读到,解决方案是在模块定义中使用状态函数。这似乎对我不起作用,我想了解原因。
这里是复合组件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>
这里是子组件(为简洁起见,计算的 props 被截断,它们只是 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>
这里是子组件(Transaction)的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;
,
但这会像以前一样返回重复项。
期待您的推荐。
【问题讨论】:
当用户向fund_transactions
添加新item时,它在parent中的空状态是什么?我怀疑你需要以不同的方式处理。
感谢 ebbishop 的快速回复。我在更新中有额外的 cmets。
我不确定我是否理解您的设计。您是否正在为每个 FundTransaction 创建和注册一个新模块?是否可以创建一个包含多个 FundTransaction 的 FundTransaction 模块?
嗨@Connor。这基本上就是我的目标。我有一个包含许多交易的基金记录。我可以将交易列表移动到 FundTransaction 模块中。
我有一个 FundRecord 模块和一个 FundTransaction 模块。
【参考方案1】:
根据我的经验,尝试将 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;
假设您的 FundTransaction 组件具有 fund_transaction
属性,您的计算属性可能如下所示。
computed:
id:
get()
return this.fund_transaction.id;
,
set(value)
this.$store.dispatch('FundModule/setId', value, id: this.fund_transaction.id);
,
,
,
【讨论】:
非常感谢您对我的支持,您的插图很有道理。也适用于具有唯一值的计算道具示例!以上是关于Vuex 可重用模块模式。使用状态功能不起作用的主要内容,如果未能解决你的问题,请参考以下文章
为啥我的链表代码在模块化可重用 JavaScript 函数中不起作用?
尝试从 Vuex 模块调度操作不起作用。 [vuex] 未知动作类型