ToddT
ToddT

Reputation: 3258

Vuex store update but DOM is not

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

Answers (2)

ToddT
ToddT

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

Boussadjra Brahim
Boussadjra Brahim

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

Related Questions