knugget
knugget

Reputation: 1

Vue2: Use form component with input type textarea to display AND edit data (without directly manipulating props)

I am building an MVP and this is the first time I do web development. I am using Vue2 and Firebase and so far, things go well.

However, I ran into a problem I cannot solve alone. I have an idea how it SHOULD work but cannot write it into code and hope you guys can help untangle my mind. By now I am incredibly confused and increasingly frustrated :D

So lets see what I got:

Child Component I have built a child component which is a form with three text-areas. To keep it simple, only one is included it my code snippets.

<template>
  <div class="wrap">
    <form class="form">
    
      <p class="label">Headline</p>
      <textarea rows="2" 
      v-model="propHeadline" 
      :readonly="readonly">
      </textarea>
      
      // To switch between read and edit
      <button
        v-if="readonly"
        @click.prevent="togglemode()">
        edit
      </button>
      <button
        v-else
        type="submit"
        @click.prevent="togglemode(), updatePost()"
      >
        save
      </button>
    </form>
  </div>
</template>

<script>
export default {
name: 'PostComponent'
  data() {
    return {
      readonly: true
    }
  },

  props: {
    propHeadline: {
      type: String,
      required: true
    }
  },
  
  methods: {
    togglemode() {
      if (this.readonly) {
        this.readonly = false
      } else {
        this.readonly = true
      }
    },
    updatePost() {
      // updates it to the API - that works
    }
  }
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
And my parent component:

<template>
  <div class="wrap">
      <PostComponent
        v-for="post in posts"
        :key="post.id"
        :knugHeadline="post.headline"
      />
  </div>
</template>

<script>
import PostComponent from '@/components/PostComponent.vue'

export default {
  components: { PostComponent },
  data() {
    return {
      posts: []
    }
  },
  created() {
    // Gets all posts from DB and pushes them in array "posts"
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

Current Status So far, everything works. I can display all posts and when clicking on "edit" I can make changes and save them. Everything gets updated to Firebase - great!

Problem / Error Message I get the following error message:

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value.

As the error says I should use a computed property based on the props value. But how can I achieve that?

Solution Approach I believe I have to use a computed getter to return the prop value - how to do that?

And then I have to use the setter to emit an event to the parent to update the value so the prop passes it back down - how to do that?

I have found bits and pieces online but by now all I see is happy families passing around small packages of data...

Would be really thankful for a suggestion on how to solve this one! :)

Thanks a lot!

Upvotes: 0

Views: 1104

Answers (1)

Esteban MANSART
Esteban MANSART

Reputation: 561

This error shows because of your v-model on texterea which mutate the prop, but in vue it is illegal to mutate props :

<textarea rows="2" 
  v-model="propHeadline" 
  :readonly="readonly">
  </textarea>

So, what you could do is to use this created() lifecycle hook and set the propHeadline prop as data :

<script>
export default {
name: 'PostComponent'
  data() {
    return {
      readonly: true,
      headline: ""
    }
  },

  props: {
    propHeadline: {
      type: String,
      required: true
    }
  },
  
  created() {
     this.headline = this.propHeadline
  }
}
</script>

An then, update the new variable on your textarea :

<textarea rows="2" 
  v-model="headline" 
  :readonly="readonly">
  </textarea>

Upvotes: 1

Related Questions