4

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>
3
  • Can you create a small demo for this using jsfiddle or snippet here to show the issue happening. No need to show ajax call just v-if else not working part. Commented Apr 5, 2020 at 4:54
  • I actually wanted to do that. Is there an easy way to make a small demo of a single file component for vue? Commented Apr 6, 2020 at 3:29
  • Yes, you can modify this demo (added in the accepted answer), as per your requirement. Commented Apr 6, 2020 at 3:41

3 Answers 3

1

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.

Sign up to request clarification or add additional context in comments.

4 Comments

OK. I see what you are saying. Actually, I'm not really modifying a prop per se, I'm just modifying an attribute of a prop which is passed to the component as a v-model. What is the correct way to do this? Should I make a copy of the object and then $emit it back to the parent?
What I ended up doing (and I don't know if this is good or bad) was just putting a flag on the axios call so that whenever it got a new value, it set a flag in the v-if.
Right, you didn't reassign the prop value, but a mutation of a property isn't allowed either. This is to ensure unidirectional data flow, a good thing. Yes: clone. And Yes: props down, emit up. Or, use Vuex, which is a lot like a global parent to emit to and take props from. Your props here could be a lot simpler too which would make this code a lot easier. Maybe better to use a token string and an id string in separate props.
Would be hard to comment with certainty about the flag without seeing how you've done it. Might be totally ok. I think most importantly the prop structure was too complex, especially {{ value.app_token[0].token }}. If you want to post the whole thing to codesandbox.io I'd be happy to take a look at it.
0

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.

1 Comment

This adds some error correction, but doesn't really address the issue. If the array is zero length, then value.app_token.length will be 0, so this is essentially the same thing.
0

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

1 Comment

That's true, but the docs vuejs.org/v2/guide/components-props.html also say "Note that objects and arrays in JavaScript are passed by reference, so if the prop is an array or object, mutating the object or array itself inside the child component will affect parent state." In this case, I was hoping to modify the object and pass it back to the parent.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.