Reputation: 3258
I've looked through all the posts about this subject and can't seem to find an answer. My Vuex store IS updating fine, but the DOM is not.
This is a screenshot of what is going on
I have a getter
called returnAmazonCredentials
returnAmazonCredentials(state) {
return state.amazonCredentials
},
I import it like this:
computed: {
...mapGetters('amazonCredentials', [
'returnAmazonCredentials',
]),
returnAmazonCredentials
is an array and so I use it in my DOM with a v-for
v-for="(cred, index) in returnAmazonCredentials"
And I also use getters
and setters
to update the elements in the array. Here is one example
sellerId: {
get() {
return this.returnAmazonCredentials.merchant_id
},
set(value) {
this.$set(this.returnAmazonCredentials[this.credIndex], 'merchant_id', value)
}
},
this.credIndex
is set when an element is clicked
When I want to remove an element from the returnAmazonCredentials
array I do this:
this.returnAmazonCredentials.splice(index, 1)
The Vuex store is updated perfectly, while the DOM still shows old data that no longer exists in the store. I've tried:
this.$nextTick() as well as this.$forceUpdate()
No luck.. Where did I go astray?
EDIT: This is all my code in my component
<template>
<v-container fluid v-resize="getHeight">
<v-row align="center" justify="center">
<v-col cols="12">
<v-card
class="purple_top"
color="#e1bee7"
>
<v-container>
<v-row>
<v-col cols="1" align="start">
<v-icon class="my_dark_purple_text pretty_icon mt-2" x-large>info</v-icon>
</v-col>
<v-col cols="11" align="start">
<h1 class="text-h6 mont bold pt-3 pb-2">You will not be charged until after you've entered all your credentials below</h1>
<h1 class="text-subtitle-1 mont font-weight-bold">After your 7 day free trial your costs will be:</h1>
<v-list class="purple_list" dense>
<v-list-item>
<v-list-item-icon class="mr-2">
<v-icon class="my_dark_purple_text">info</v-icon>
</v-list-item-icon>
<v-list-item-content>
<div class="text-body-1 mont">$25/month for the first marketplace</div>
</v-list-item-content>
</v-list-item>
<v-list-item>
<v-list-item-icon class="mr-2">
<v-icon class="my_dark_purple_text">info</v-icon>
</v-list-item-icon>
<v-list-item-content>
<div class="text-body-1 mont">$20/month for each additional marketplace</div>
</v-list-item-content>
</v-list-item>
</v-list>
</v-col>
</v-row>
</v-container>
</v-card>
</v-col>
</v-row>
<v-row>
<v-col cols="8">
<v-card
color="#f1e7df"
id="devIds"
>
<v-card-title>
<v-icon class="my_dark_purple_text">vpn_key</v-icon>
<h1 class="text-h6 oswald my_dark_purple_text pl-2">DEVELOPMENT IDS</h1>
</v-card-title>
<v-container>
<v-row>
<v-col cols="6" class="ml-10">
<h1 class="text-h6 mont">US, Canada, Mexico</h1>
</v-col>
<v-col cols="5">
<h1 class="text-h6 mont">UK and the EU</h1>
</v-col>
</v-row>
<v-row>
<v-col cols="3" class="ml-10">
<v-text-field readonly solo :value="us" class="mont"></v-text-field>
</v-col>
<v-col cols="3">
<v-btn height="48px" class="my_dark_purple_btn ml-n7" @click="copyCreds(us)">Copy</v-btn>
</v-col>
<v-col cols="3" class="">
<v-text-field readonly solo :value="eu" class="mont"></v-text-field>
</v-col>
<v-col cols="2">
<v-btn height="48px" class="my_dark_purple_btn ml-n7" @click="copyCreds(eu)">Copy</v-btn>
</v-col>
</v-row>
<v-row>
<v-col cols="6" class="ml-10">
<h1 class="text-h6 mont">Australia</h1>
</v-col>
<v-col cols="5">
<h1 class="text-h6 mont">Japan</h1>
</v-col>
</v-row>
<v-row>
<v-col cols="3" class="ml-10">
<v-text-field readonly solo :value="us" class="mont"></v-text-field>
</v-col>
<v-col cols="3">
<v-btn height="48px" class="my_dark_purple_btn ml-n7" @click="copyCreds(australia)">Copy</v-btn>
</v-col>
<v-col cols="3" class="">
<v-text-field readonly solo :value="eu" class="mont"></v-text-field>
</v-col>
<v-col cols="2">
<v-btn height="48px" class="my_dark_purple_btn ml-n7" @click="copyCreds(japan)">Copy</v-btn>
</v-col>
</v-row>
</v-container>
</v-card>
</v-col>
<v-col cols="4">
<v-card
color="#d9d6e1"
:height="matchingHeights + 'px'"
>
<v-container fluid fill-height>
<v-row class="pt-2">
<v-col cols="12" justify="center" align="center">
<a href="https://youtu.be/-iWlFyX0254" target="_blank">
<v-img
src="how.png"
max-width="153px"
contain
></v-img>
</a>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<h1 class="mont text-h5 text-center">Do I retrieve my US Amazon credentials?</h1>
</v-col>
</v-row>
<v-row>
<v-divider class="mx-4"></v-divider>
</v-row>
<v-row class="pt-2">
<v-col cols="12" justify="center" align="center">
<a href="https://youtu.be/p4RwqegRc9s" target="_blank">
<v-img
src="how.png"
max-width="153px"
contain
></v-img>
</a>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<h1 class="mont text-h5 text-center">Do I retrieve my Amazon credentials outside of the US?</h1>
</v-col>
</v-row>
</v-container>
</v-card>
</v-col>
</v-row>
<p>credentials are {{ returnAmazonCredentials }}</p>
<v-row
v-for="(cred, index) in returnAmazonCredentials"
:key="index"
>
<v-col cols="12">
<v-card
color="#e9daea"
>
<v-card-title>
<v-icon class="my_dark_purple_text">language</v-icon>
<h1 class="text-h6 oswald my_dark_purple_text pl-2">ENTER YOUR AMAZON CREDENTIALS BELOW</h1>
</v-card-title>
<v-container>
<v-form>
<v-row>
<v-col cols="6">
<v-row>
<v-col cols="12">
<v-text-field
color="#6a0080"
label="Amazon Seller Id"
prepend-icon="person"
v-model="sellerId"
@click="setIndex(index)"
:rules="[() => !!returnAmazonCredentials[credIndex]['merchant_id'] || 'Please provide your Amazon seller id', sellerIdValidation(!!returnAmazonCredentials[credIndex]['merchant_id']) ]"
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-select
:items="marketplaces"
label="Select your Amazon Marketplace"
color="#6a0080"
prepend-icon="map"
v-model="marketplace"
@click="setIndex(index)"
:rules="[() => !!returnAmazonCredentials[credIndex]['marketplace'] || 'Please select your Amazon marketplace', marketValidation(!!returnAmazonCredentials[credIndex]['marketplace']) ]"
></v-select>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-text-field
color="#6a0080"
label="Amazon Auth Token"
prepend-icon="https"
v-model="authToken"
@click="setIndex(index)"
:rules="[() => !!returnAmazonCredentials[credIndex]['auth_token'] || 'Please provide your Amazon auth token', authTokenValidation(!!returnAmazonCredentials[credIndex]['auth_token']) ]"
></v-text-field>
</v-col>
</v-row>
</v-col>
<v-col cols="1">
<v-divider vertical></v-divider>
</v-col>
<v-col cols="5">
<v-row class="mt-1">
<v-btn
color="#68007d"
outlined
block
x-large
:disabled="_.some(errors, (element) => _.includes(element, index))"
@click="saveMarketplace"
:loading="saveLoading"
>
<v-icon
left
>
check_circle
</v-icon>
SAVE THIS MARKETPLACE
</v-btn>
</v-row>
<v-row class="mt-11">
<v-btn
block
x-large
outlined
color="#388E3C"
@click="addMarketplace"
>
<v-icon
left
>
add_circle
</v-icon>
ADD ANOTHER MARKETPLACE
</v-btn>
</v-row>
<v-row class="mt-11">
<v-btn
block
x-large
outlined
color="#B71C1C"
:disabled="returnAmazonCredentials.length == 1"
@click="removeMarketplace(index)"
>
<v-icon
left
>
remove_circle
</v-icon>
REMOVE MARKETPLACE
</v-btn>
</v-row>
</v-col>
</v-row>
</v-form>
<v-row v-if="(returnAmazonCredentials.length - 1) == index">
<v-col cols="12">
<v-btn
class="white--text"
color="#68007d"
block
x-large
@click="sendCreds"
:loading="finishedLoading"
:disabled="errors.length > 0"
id="custom-disabled"
>
FINISHED
</v-btn>
</v-col>
</v-row>
</v-container>
</v-card>
</v-col>
</v-row>
<v-dialog
v-model="failDialog"
max-width="600px"
persistent
>
<v-card>
<v-card-title>
<span class="title font-weight-bold">Oh snap! It looks like your credentials are incorrect</span>
</v-card-title>
<v-card-text>
<v-row>
<v-col cols="12">
<h1
class="subtitle-1"
style="color: black !important;"
>Amazon let us know the following error:</h1>
</v-col>
</v-row>
<v-row
:class="spacing"
v-for="(reason, index) in failReasons"
:key="reason.marketplace"
>
<v-col cols="12">
<h1
class="subtitle-1"
>Your credentials failed in this marketplace: <strong>{{ reason.marketplace }}</strong></h1>
</v-col>
<v-col cols="12" class="mt-n5">
<h1
class="subtitle-1"
>For this reason: <strong>{{ reason.reason }}</strong></h1>
</v-col>
<v-col
cols="12"
justify="center"
align="center"
>
<a :href="whichVideoToShow(reason.marketplace)" target="_blank">
<v-img
src="blank_how.png"
max-width="100px"
contain
></v-img>
</a>
</v-col>
<v-col cols="12" class="mt-n5">
<h1 class="subtitle-1 text-center">Do I retrieve my Amazon credentials?</h1>
</v-col>
<v-divider class="mx-4 mt-3"></v-divider>
</v-row>
</v-card-text>
<v-card-actions class="">
<v-spacer></v-spacer>
<v-btn color="#68007d" large class="white--text" @click="closeDialog">I UNDERSTAND</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-container>
</template>
<script>
import axios from 'axios';
import { mapGetters, mapActions } from 'vuex'
export default {
data: function() {
return {
failDialog: false,
australia: "6078-0648-3237",
us: "7531-9706-4825",
eu: "4048-6340-0484",
japan: "1447-5438-8862",
matchingHeights: 295,
marketplaces:[
{ text: 'Australia', value: "A39IBJ37TRP1C6" },
{ text: 'Canada', value: "A2EUQ1WTGCTBG2" },
{ text: 'France', value: "A13V1IB3VIYZZH" },
{ text: 'Germany', value: "A1PA6795UKMFR9" },
{ text: 'Italy', value: "APJ6JRA9NG5V4" },
{ text: 'Japan', value: "A1VC38T7YXB528" },
{ text: 'Mexico', value: "A1AM78C64UM0Y8" },
{ text: 'Spain', value: "A1RKKUPIHCS9HS" },
{ text: 'United Kingdom', value: "A1F83G8C2ARO7P" },
{ text: 'United States', value: "ATVPDKIKX0DER" },
],
credIndex: 0,
errors: [],
finishedLoading: false,
saveLoading: false,
amazonError: '',
failReasons: [],
};
},
async mounted() {
await this.getRemoteAmazonCredentials()
},
computed: {
...mapGetters('amazonCredentials', [
'returnAmazonCredentials',
]),
sellerId: {
get() {
return this.returnAmazonCredentials.merchant_id
},
set(value) {
this.$set(this.returnAmazonCredentials[this.credIndex], 'merchant_id', value)
}
},
marketplace: {
get() {
return this.returnAmazonCredentials.marketplace
},
set(value) {
this.$set(this.returnAmazonCredentials[this.credIndex], 'marketplace', value)
}
},
authToken: {
get() {
return this.returnAmazonCredentials.auth_token
},
set(value) {
this.$set(this.returnAmazonCredentials[this.credIndex], 'auth_token', value)
}
}
},
methods: {
...mapActions('amazonCredentials', [
'getRemoteAmazonCredentials',
'testRemoteAmazonCredentials',
'removeAmazonCredential'
]),
setIndex(index) {
this.credIndex = index
},
getHeight() {
var devCard = document.getElementById("devIds");
this.matchingHeights = devCard.offsetHeight;
},
copyCreds(country) {
this.$clipboard(country);
},
sellerIdValidation(value) {
if(value){
_.pull(this.errors, `${this.credIndex}-id`)
}
else {
if(!_.includes(this.errors, `${this.credIndex}-id`)) {
this.errors.push(`${this.credIndex}-id`)
}
}
return true
},
marketValidation(value) {
if(value){
_.pull(this.errors, `${this.credIndex}-market`)
}
else {
if(!_.includes(this.errors, `${this.credIndex}-market`)) {
this.errors.push(`${this.credIndex}-market`)
}
}
return true
},
authTokenValidation(value) {
if(value){
_.pull(this.errors, `${this.credIndex}-auth`)
}
else {
if(!_.includes(this.errors, `${this.credIndex}-auth`)) {
this.errors.push(`${this.credIndex}-auth`)
}
}
return true
},
addMarketplace() {
this.returnAmazonCredentials.push({})
},
removeMarketplace(index) {
this.removeAmazonCredential(index)
this.credIndex = 0
},
async sendCreds() {
this.failReasons = []
this.finishedLoading = true
let me = this.returnAmazonCredentials
let response = await this.testRemoteAmazonCredentials()
this.addRemoveTestFailure(response)
},
async saveMarketplace() {
this.failReasons = []
this.saveLoading = true
let response = await this.testRemoteAmazonCredentials()
this.addRemoveTestFailure(response)
},
addRemoveTestFailure(response) {
response.forEach(element => {
if(element.result == "fail") {
this.failDialog = true
let failure = {
reason: element.reason,
marketplace: element.marketplace
}
this.failReasons.push(failure)
}
})
this.saveLoading = false
this.finishedLoading = false
},
closeDialog() {
this.failDialog = false
this.saveLoading = false
},
whichVideoToShow(marketplace) {
if(marketplace == "United States" || marketplace == "Mexico" || marketplace == "Canada") {
return 'https://youtu.be/-iWlFyX0254'
}
else {
return 'https://youtu.be/p4RwqegRc9s'
}
},
spacing() {
if(this.failReasons.length >= 1) {
return mt-n4
}
}
}
};
</script>
<style scoped>
@import '../../styles/global_styles.css';
#custom-disabled.v-btn--disabled {
background-color: rgba(104,0,125,.5) !important;
color: white !important;
}
</style>
I did add this mutation
in my store to remove the element
removeCredential: (state, payload) => {
state.amazonCredentials.splice(payload, 1)
},
And this is the method that I've updated in the component itself:
removeMarketplace(index) {
this.removeAmazonCredential(index)
this.credIndex = 0
},
Where I send the index to be removed from the credentials array.
But yea I was already doing that in the component itself, and I'm getting the same effect. Data updated while the DOM is not..
Upvotes: 0
Views: 247
Reputation: 3258
Wow, the answer surprises me. Its basically that if you have an array of objects in your Vuex store, you should NOT use getters / setters if you ALSO are stepping over that array in a v-for
instead you can just get / set the values directly.
For example. If you do this:
<v-row
v-for="(cred, index) in myVuexStoreArrayOfObjects"
:key="index"
>
Then you should do this for your v-model
<v-text-field
v-model="cred.value"
></v-text-field>
So that finally you can remove an object from that array and maintain your reactivity. Like so:
removeObjectFromMyVuexStoreArray(index) {
this.myVuexStoreArrayOfObjects.splice(index, 1)
},
And there is no need to get/set
. In fact if you get/set
you will NOT be able to remove an item and remain reactive.
Upvotes: 1
Reputation: 1
You should dispatch an action that remove that item :
sellerId: {
get() {
return this.returnAmazonCredentials.merchant_id
},
set(value) {
this.$store.dispatch('removeCredential',this.credIndex)
}
},
store :
mutations: {
REMOVE_CREDENTIAL (state,index) {
state.amazonCredentials.splice(index, 1)
}
},
actions: {
removeCredential(context,index) {
context.commit('REMOVE_CREDENTIAL',index)
}
Upvotes: 3