Home > Back-end >  Vue.js 3: how to pre-populate bound form with props
Vue.js 3: how to pre-populate bound form with props

Time:05-31

I am trying to create a simple CRUD app with Vuejs 3.

I have a homepage with a form (as a child component) and a table with created items (as another child component). I submit data via the form to API/database and the table updates. So far so good.

Then, for the update phase, I would like to have a detail page for each item where I also would have the form (the same component reused). But the idea is that form fields would be pre-populated with data from API/Database.

The table on the homepage has a route-link to a detail page and I am passing the id of the item as params. The detail page makes request to API based on id, receives item data and passes them as props into the form component.

If I try to render data directly into template like this, it works fine:

<p v-if="submitType === 'update' && item.id">{{ item.id }}</p>

Now, form fields are bound by v-model to data (form.id for example). But when I try to repopulate it as below, I always get undefined values.

  data() {
    return {
      form: {
        id: this.submitType === 'update' ? this.item.id : 0,
      }
    }
  },

I suspect that problem is that the parent call to API is asynchronous and the passing of props is delayed. Because when I pass as props some hardcoded value, it appears as a value in the form field with no problem. Also if the form is shown only when props are received (with the v-if directive), the data.form.id is still undefined.

So is there any way how to pre-populate bound form fields with received props and still have the form component reused for insert and update actions? The rest of the relevant code is below. Thank you very much in advance

// Detail Page
<template>
  <Form :item="item" submit-type="update"></Form>
</template>

<script>
export default {
  data() {
    return {
      item: {}
    }
  },
  created() {
    callAPI(id).then( response => this.item = response.data )
  }
}
</script>
// Form Component
<template>
  <p v-if="submitType === 'update' && item.id">{{ item.id }}</p>

  <div v-if="submitType === 'insert' || (submitType === 'update' && item.id )">
    <section>
      <form @submit.prevent="onSubmit">

        <div>
          <label for="id">ID</label>
          <input id="id" name="id" v-model="form.id"  type="number" placeholder="ID">
        </div>

        <input type="submit" value="Save">

      </form>
    </section>
  </div>
</template>

<script>
export default {
  name: 'Form',
  props: {
    item: {
      type: Object
    },
    submitType: {
      type: String
    }
  },
  data() {
    return {
      form: {
        id: this.submitType === 'update' ? this.item.id : 0,
      }
    }
  },
}
</script>

CodePudding user response:

You can try with watchers, take a look at following snippet:

const app = Vue.createApp({
  data() {
    return {
      item: {},
      type: 'update'
    }
  },
  methods: {
    change() {
      this.type === 'update' ? this.type = 'insert' : this.type = 'update'
    }
  },
  created() {
    fetch('https://jsonplaceholder.typicode.com/todos/1')
      .then(response => response.json())
      .then(json => this.item = json)
    //callAPI(id).then( response => this.item = response.data )
  }
})
app.component('myForm', {
  template: `
  <p v-if="submitType === 'update' && item.id">{{ item.id }}</p>
  <div v-if="submitType === 'insert' || (submitType === 'update' && item.id )">
    <section>
      <form @submit.prevent="onSubmit">
        <div>
          <label for="id">ID</label>
          <input id="id" name="id" v-model="form.id"  type="number" placeholder="ID">
        </div>
        <input type="submit" value="Save">
      </form>
    </section>
  </div>
  `,
  props: {
    item: {
      type: Object
    },
    submitType: {
      type: String
    }
  },
  data() {
    return {
      form: {}
    }
  },
  methods: {
    fillData() {
      this.submitType === 'update' ? this.form = {...this.item} : this.form = {id: 0}
    }
  },
  watch: {
    item() {
      this.fillData()
    },
    submitType() {
      this.fillData()
    }
  },
})
app.mount('#demo')
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<div id="demo">
  <button @click="change">switch type</button>
  {{type}}
  <my-form :item="item" :submit-type="type"></my-form>
</div>

  • Related