mankowitz
mankowitz

Reputation: 2051

VueJS not responding to change in array length?

Using a single-file template, I would like to have a button that shows "click here" if an array is empty and shows the value if it is populated. The ajax is working fine and the object is getting updated, but the button doesn't re-render. What am I doing wrong here?

<template>
  <span>
    <button
      v-if="value.app_token.length"
      class="btn btn-outline-primary btn-sm"
      disabled
    >Mobile App Token: {{ value.app_token[0].token }}</button>
    <button v-else class="btn btn-primary btn-sm" @click="getToken">Click to get Mobile App Token</button>
  </span>
</template>

<script>
module.exports = {
  props: ["value"],

  methods: {
    getToken() {
      axios
        .get("/patients/getToken/" + this.value.id)
        .then(response =>
          this.value.app_token.splice(
            0,
            this.value.app_token.length,
            response.data
          )
        )
        .catch(error => console.log(error));
    }
  }
};
</script>

Upvotes: 4

Views: 2734

Answers (3)

You should use Vue data instead of props, changing props on the component itself is anti-pattern. Check the documentation https://v2.vuejs.org/v2/guide/components.html

Upvotes: 0

Dan
Dan

Reputation: 63099

The getToken method mutates the value prop, which is not allowed (you may have an error in the console).

Solution

Create a computed for the token and a variable to hold the fetched data:

data() {
  return {
    fetched: null
  }
},
computed: {
  token() {
    let token = JSON.parse(JSON.stringify(this.value.app_token));
    if (this.fetched) {
      // Now this mutates a copy, not the original
      token.splice(0, token.length, this.fetched);
    }
    return token;  
  }
}

If nothing has been fetched, the token value will be the same as in the prop. Otherwise, it will recalculate to the modified token. Change your getToken method so that its purpose is to store the data result in fetched:

methods: {
  getToken() {
    axios
      .get("/patients/getToken/" + this.value.id)
      .then(response => this.fetched = response.data)
      .catch(error => console.log(error));
  }
}

Now use the computed in your v-if:

v-if="token.length"

And display it like:

{{ token[0].token }}

Here is a demo for you

Also, this would be much less confusing if the token prop was just the token string by itself instead of part of a complex object, and then also pass the axios id as a separate prop.

Upvotes: 1

Rijosh
Rijosh

Reputation: 1544

Change the condition to

v-if="value.app_token && value.app_token.length >0"

If the value.app_token in an empty array, then there will be a length of 0 which will make the condition true.

Upvotes: 0

Related Questions