Nathan
Nathan

Reputation: 125

Vue - Change computed property value without changing parent property value

This question is probably going to seem quite basic (probably because it is), but for some reason I'm having difficulty figuring out how to handle it. Basically, I have a property which is passed to my Vue component which dictates some style differences in the component. With that being said, I have a button inside the component which I want to change this property. With Vue, obviously, I cannot change the parent property value directly (Vue gets mad), but I can't figure out how to compute a dynamic property without using the initial property value, which will make it unable to be changed. I'm sure there's a simple solution, so hopefully someone can point out my stupidity.

(for the sake of simplicity I've removed the rest of the component, as this issue only involves a few parts

Here's the component:

<template>
    <div>
      <button type="button" :style="{
           backgroundColor: isSaved ? 'gray' : '#68AEFA',
      }" v-on:click="savePost()">
         {{ isSaved ? 'Unsave' : 'Save' }} 
      </button>
    </div>
</template>
<script>
   export default {
      props: ['saved'],
      methods:{
        savePost: function() {
           // Want to change value of isSaved here
        },
      },
      computed: {
         isSaved: function () {
             if (this.saved == '0') {
                return false
             } else {
                return true
             }
         }
      },
   }
</script>

Hopefully the code above shows a bit better what I'm describing. Essentially, isSaved is dependent on the initial value of the property 'saved', which it deduces its value from. Since Vue doesn't allow me to change the value of saved directly, I initialized another variable isSaved which is computed from the initial saved property value, but how can I change isSaved if its value is based on the value of 'saved'?

Again, sorry for the stupidity of the question but I couldn't really see anything in the Vue documents on how I would do this, as it relies on on-page-load computed properties without much description of how you would handle adjusting the value.

Upvotes: 0

Views: 2384

Answers (2)

Tony
Tony

Reputation: 1432

I know this already has an accepted answer, but I personally think adding a function as a prop should be an anti-pattern. It will work, but it rubs me the wrong way.

You were on the right path before. According to the Vue documentation, there are two instances when it will be tempting to mutate a prop

There are usually two cases where it’s tempting to mutate a prop:

  • The prop is used to pass in an initial value; the child component wants to use it as a local data property afterwards. In this case, it’s best to define a local data property that uses the prop as its initial value:

props: ['initialCounter'],
data: function () {
  return {
    counter: this.initialCounter
  }
}

  • The prop is passed in as a raw value that needs to be transformed. In this case, it’s best to define a computed property using the prop’s value:

props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

I think you are better off using your earlier method. Passing a function as props is a bit messy, in my opinion.

EDIT --- this article confirms my hunch regarding passing functions as props. It is a bad idea. The article offers alternatives. I suggest you give it a read.

Upvotes: 2

Leonardo Bezerra
Leonardo Bezerra

Reputation: 735

When using computed properties you have 2 inner methods, get and set, but in this case your computed property reflects from props, so if you rerender the parent component it will be set back to the parent value, in this case "0".

You can send a method from the parent to change the value using props.

<template>
    <ChildComponent  :save="save" :isSaved="isSaved"/>
</template>

<script>

export default {
   name: "ParentComponent"
   data() {
      return {
         isSaved: false
      }
   }
    methods: {
       save() {
          this.isSaved = true;
       }
    }
};
</script>

You just need to set the save prop as function in the child component and call it.

<template>
    <button
        type="button"
        :style="{
           backgroundColor: isSaved ? 'gray' : '#68AEFA',
      }"
        @click="save()"
    >{{ isSaved ? 'Unsave' : 'Save' }}</button>
</template>

<script>

export default {
   name: "ChildComponent",
   data() {
      return {
         saved: false
      }
   }
   computed: {
      uniqueSaveStatus: {
         get() {
            return this.saved
         },
         set(v) {
            this.saved = v
         }
      }
   }
   props: {
      isSaved: Boolean,
      save: { type: Function }
   }
};
</script>

So if you really need the isSaved variable to be unique, you need to define it in the childComponent like uniqueSaveStatus.

Upvotes: 1

Related Questions