0

i have a component that pulls data from db and display it in bootstrap cards. when i click the edit button the modal shown and passed to the input. when i change the input value, the card title changes as well, and i find no way to prevent it, while i didn't even coded it.

my guess is that the issue is with the v-model but i couldn't find any other way to pass my data to the modal edit form. i'm a vuejs newbie so it might be related to that

i don't look for an explanation about how reference object works, i wonder why the DOM getting rendered and how can i prevent this behavior

my component attached below

<template>
    <div>
        <div class="container">
          <div class="row">
            <div class="col-12">
              <p>
                <b-button id="show-btn" variant="primary" v-b-modal.add-product v-on:click="addProduct()"><font-awesome-icon icon="plus" /> Create Product</b-button>
              </p>
            </div>
          </div>
          <div class="row">
              <b-card
                :title="product.title"
                :img-src="product.image"
                img-alt="Image"
                img-class="product_image"
                img-top
                tag="article"
                class="col-4 mb-2"
                v-for="product in products" :key="product.id"
              >
              
                <div>
                  <b-button href="#" variant="outline-primary" v-b-modal.add-product  v-on:click="editProduct(product)">Edit</b-button>
                </div>
              </b-card>
          </div>
        </div>

        <b-modal ref="add-product" id="add-product" hide-footer title="create/edit product" size="xl" >
         <div class="d-block text-center">
          <div v-if="!edit">
            <form v-on:submit.prevent="create" enctype="multipart/form-data">
            <div class="row">
              <div class="col-12 col-md-6">
                <div class="form-group">
                  <label>Product Name</label>
                  <input type="text" name="product_name" id="product_name" v-model="form.title"  class="form-control" /><br>
                </div>
              </div>
              <div class="col-12 col-md-6">
                <div class="form-group">
                  <label>Picture</label>
                  <input class="form-control" type="file" ref="file" @change="onSelect" />
                </div>
              </div>
            </div>
               <input type="submit" value="save" class="btn btn-primary" />
             </form>
           </div>
          <div v-else>
            <form v-on:submit.prevent="save" enctype="multipart/form-data">
            <div class="row">
              <div class="col-12 col-md-6">
                <div class="form-group">
                  <label>Create Product</label>
                  <input type="text" name="product_name" id="product_name_edit" v-model="productEdit.title"  class="form-control" /><br>
                </div>
              </div>
              <div class="col-12 col-md-6">
                <div class="form-group">
                  <label>Picture</label>
                  <input class="form-control" type="file" ref="file" @change="onSelect" />
                </div>
              </div>
            </div>
            <input type="submit" value="Save" class="btn btn-primary" />
            </form>
          </div>
         </div>
       </b-modal>
    </div>
</template>
<script>
import { library } from '@fortawesome/fontawesome-svg-core'
import { faPlus } from '@fortawesome/free-solid-svg-icons'
library.add(faPlus)
import axios from "axios"
import router from "../router"

export default {
    name: "Products",
    components:{
    },
    data() {
      return {
        products:[],
        form: {
          title: ''
        },
        productEdit: '',
        
        edit: false,
      }
    },
    methods: {
      del() {
        
      addProduct() {
          this.edit = false
      },
      editProduct(p) {
        this.edit = true;
        this.productEdit = p;
      },
      onSelect(){
        const allowedTypes = ["image/jpeg", "image/jpg", "image/png"];
        const file = this.$refs.file.files[0];
        this.file = file;
        if(!allowedTypes.includes(file.type)){
          this.$swal('oops,'image only,'error')
        }
       
      },
      async create() {
        const formData = new FormData();
        formData.append('file',this.file);
        formData.append('title',this.form.title);
        try{
          await axios.post('/api/createproduct',formData).then(function(response){
            if (response.data.status=="ok") {
              this.getProducts()
            }
          });
        }
        catch(err){
          console.log(err);
          this.message = err.response.data.error
        }
      },
      showModal() {
        this.$refs['add-product'].show()
      },
      hideModal() {
        this.$refs['add-product'].hide()
      },
      toggleModal() {
        // We pass the ID of the button that we want to return focus to
        // when the modal has hidden
        this.$refs['add-product'].toggle('#toggle-btn')
      },
     
      getProducts() {
        axios.get("/api/products").then((response) => {
          response.data.forEach((item) => {
            this.products.push(item)
          });
        }).catch((errors) => {
            console.log(errors)
        })
      }
    },
    mounted() {
        this.getProducts()
    }
}

</script>
sd1sd1
  • 1,028
  • 3
  • 15
  • 28
  • Does this answer your question? [Is JavaScript a pass-by-reference or pass-by-value language?](https://stackoverflow.com/questions/518000/is-javascript-a-pass-by-reference-or-pass-by-value-language) – Michal Levý Aug 26 '21 at 19:52
  • i understand the refernce, but i wonder why it update the dom and how can this be prevented or it's built in to the vue mechanism? – sd1sd1 Aug 26 '21 at 20:04
  • It's not about Vue at all. You are using same data (same object - thanks to `this.productEdit = p`) both to render the card and as a `v-model` in the input. So when `v-model` changes the title (technically user does this), changed value is reflected in the card. Simple. Only way to prevent that is to not use same object ... – Michal Levý Aug 26 '21 at 20:43

1 Answers1

1

There is few patterns that you might use in that scenario when it comes to edit entities in modals, the most simple IMO is simply pass the entity data to the modal component and then clone the data internally.

That way the changes aren't reflected on the original entity. Once you are willing to apply the changes, simply emit the new entity and merge/replace the original. Bonus - you can cancel the modal and no changes will be saved.

There are more complicated patterns like storing all the changes in array (useful when you want undo/redo functionality) or uncontrolled component pattern (the data stored by the DOM itself) but this is for another discussion.

Evgeni Dikerman
  • 486
  • 3
  • 18