[Vuejs]-Parent component only pass the dynamic v-modal ID to the child of the first page in pagination

3👍

The issue is that Vue is trying to reuse your modal on page 1, to save how much it has to re-render.

The root of your issue is that inside the modal component, you set this.customer to this.selectedCustomer in the create hook and no where else. Since the create doesn’t run again on page 2, it wont update this.customer meaning the ID will still match the rows from page 1.

In the long term it might be a good idea for for you to rethink that logic.

But you should be able to solve the issue by adding :key="data.item.id" to the customer-modal, which forces Vue to re-render the modal when the id updates. So it can’t try to reuse the modals across the pages.

<customer-modal  modalType="edit" :selectedCustomer="data.item" :customers="customers" @update-customer="data.item = $event" :key="data.item.id"></customer-modal>

Working snippet (based on codepen from this Github Issue.

Vue.use(window.vuelidate.default);
const { required, email, numeric, minLength, maxLength } = window.validators;
let OMDModal = {
  props: ["selectedCustomer", "modalType", "customers"],
  template: '#customer-modal',
  validations: {
    customer: {
      first_name: {
        required,
        minLen: minLength(3),
        maxLen: maxLength(12)
      },
      last_name: {
        required,
        minLen: minLength(3),
        maxLen: maxLength(12)
      },
      email: {
        required,
        email,
        isUnique: function (val) {
          if (val === "") return true;
          else if (val === this.currentEmail) return true;
          return axios.get(`api/validateEmail/${val}`).then((res) => {
            return res.data.unique;
          });
        }
      }
    }
  },
  data() {
    return {
      currentEmail: "",
      alert_sec: 0,
      alert_duration: 5,
      alert_message: "",
      alert_color: "",
      customer: {
        id: "",
        first_name: "",
        last_name: "",
        email: ""
      },
      customer_default: {
        first_name: [],
        last_name: [],
        email: []
      }
    };
  },
  watch: {
    "customer.first_name"(newVal, oldVal) {
      if (this.modalType === "edit") {
        this.customer_default.first_name.push(oldVal);
      }
    },
    "customer.last_name"(newVal, oldVal) {
      if (this.modalType === "edit") {
        this.customer_default.last_name.push(oldVal);
      }
    },
    "customer.email"(newVal, oldVal) {
      if (this.modalType === "edit") {
        this.customer_default.email.push(oldVal);
      }
    }
  },
  computed: {
    ModalID() {
      if (this.modalType === "create") {
        return "customerModal";
      } else {
        return this.customer.first_name.replace(/\s/g, "") + this.customer.id;
      }
    },
    now() {
      const monthNames = [
        "Jan",
        "Feb",
        "Mar",
        "Apr",
        "May",
        "Jun",
        "Jul",
        "Aug",
        "Sep",
        "Oct",
        "Nov",
        "Dec"
      ];
      const today = new Date();
      let day = today.getDate().toString();
      let month = (today.getMonth() + 1).toString();
      let year = today.getFullYear().toString();
      return year + "-" + month + "-" + day;
    },
    first_nameValidate() {
      if (this.$v.customer.first_name.$dirty) {
        return !this.$v.customer.first_name.$anyError;
      } else return null;
    },
    last_nameValidate() {
      if (this.$v.customer.last_name.$dirty) {
        return !this.$v.customer.last_name.$anyError;
      } else return null;
    },
    emailValidate() {
      if (this.$v.customer.email.$dirty) {
        return !this.$v.customer.email.$anyError;
      } else return null;
    }
  },
  methods: {
    setFirstName(e) {
      this.customer.first_name = e;
      this.$v.customer.first_name.$touch();
    },
    setLastName(e) {
      this.customer.last_name = e;
      this.$v.customer.last_name.$touch();
    },
    setEmail(e) {
      this.customer.email = e;
      this.$v.customer.email.$touch();
    },
    resetModal() {
      this.$nextTick(() => {
        this.$v.$reset();
      });
      if (this.modalType === "create") {
        this.customer = {
          id: "",
          first_name: "",
          last_name: "",
          email: ""
        };
      }

      if (this.alert_color != "success") {
        if (this.customer_default.first_name[1] != undefined) {
          this.customer.first_name = this.customer_default.first_name[1];
        }
        if (this.customer_default.last_name[1] != undefined) {
          this.customer.last_name = this.customer_default.last_name[1];
        }
        if (this.customer_default.email[1] != undefined) {
          this.customer.email = this.customer_default.email[1];
        }
      }
    },
    onSubmit(v, e) {
      v.customer.$touch();
      if (this.$v.customer.$anyError) {
        this.alert_message = "Correct the inputs Please";
        this.alert_color = "danger";
        this.show_alert();
      } else {
        this.customer.first_name =
          this.customer.first_name.charAt(0).toUpperCase() +
          this.customer.first_name.slice(1);
        // CREATE CUSTOMER
        if (this.modalType === "create") {
          this.customer.created_at = this.now;
          let last_index = this.customers.length - 1;
          let last_customer = this.customers[last_index];
          this.customer.id = last_customer.id + 1;
          fetch(`api/customers`, {
            method: "POST",
            body: JSON.stringify(this.customer),
            headers: { "content-type": "application/json" }
          })
            .then((res) => res.json())
            .then((res) => {
              this.$nextTick(() => {
                this.$v.$reset();
              });
              this.$bvModal.hide(
                this.customer.first_name.replace(/\s/g, "") +
                  this.customer.id || "customerModal"
              );
              this.$emit("create-customer", this.customer);
              this.customer = {
                id: "",
                first_name: "",
                last_name: "",
                email: ""
              };
            })
            .catch((e) => console.log(e));
        } else {
          // UPDATE CUSTOMER
          this.customer.updated_at = this.now;
          fetch(`api/customers/${this.customer.id}`, {
            method: "put",
            body: JSON.stringify(this.customer),
            headers: {
              "content-type": "application/json"
            }
          })
            .then((res) => res.json())
            .then((data) => {
              this.$nextTick(() => {
                this.$v.$reset();
                this.$bvModal.hide(
                  this.customer.first_name.replace(/\s/g, "") +
                    this.customer.id || "customerModal"
                );
              });
              this.$emit("update-customer", this.selectedCustomer);
              this.alert_color = "success";
              this.alert_message = "Customer Updated Successfully";
            })
            .catch((error) => console.log(error));
        }
      }
    },
    show_alert() {
      this.alert_sec = this.alert_duration;
    },
    countDown(alert_sec) {
      this.alert_sec = alert_sec;
    }
  },
  created() {
    if (this.modalType === "edit") {
      this.customer = this.selectedCustomer;
      this.currentEmail = this.selectedCustomer.email;
    }
  }
};


window.onload = () => {
  new Vue({
    el: "#app",
    data() {
      return {
        per_page: 3,
        current_page: 1,
        fields: [
          { key: "#", sortable: false },
          { key: "id", sortable: true },
          { key: "first_name", label: "Name", sortable: true, class: "w-25" },
          { key: "email", sortable: true },
          { key: "actions", sortable: false }
        ],
        sort_by: "email",
        sort_desc: false,
        filter: null,
        filter_on: [],
        customers: [],
        customer: {
          id: "",
          first_name: "",
          last_name: "",
          email: ""
        }
      };
    },
    computed: {
      now() {
        const monthNames = [
          "Jan",
          "Feb",
          "Mar",
          "Apr",
          "May",
          "Jun",
          "Jul",
          "Aug",
          "Sep",
          "Oct",
          "Nov",
          "Dec"
        ];
        const today = new Date();
        let day = today.getDate().toString();
        let month = (today.getMonth() + 1).toString();
        let year = today.getFullYear().toString();
        return year + "-" + month + "-" + day;
      },
      rows() {
        return this.customers.length;
      }
    },
    methods: {
      fetchCustomers() {
        fetch("https://reqres.in/api/users")
          .then((res) => res.json())
          .then((res) => {
            this.customers = res.data;
          })
          .catch((err) => console.log(err));
      },
      on_filtered(filtered_items) {
        this.rows;
      },
      openModal(id) {
        this.$root.$emit('bv::show::modal', id)
      }
    },
    created() {
      this.fetchCustomers();
    },
    components: {
      "omd-modal": OMDModal
    }
  });
};
<link href="https://unpkg.com/bootstrap@4.5.0/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap-vue@2.15.0/dist/bootstrap-vue.css" rel="stylesheet" />

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue@2.15.0/dist/bootstrap-vue.js"></script>


<div id="app">
  <div class="card">
    <div class="card-body">
      <!-- Card Header (Title & Create btn) -->
      <div class="row ">
        <div class="col-sm-4">
          <h4 class="card-title font-weight-bold">
            User Management
          </h4>
        </div>
        <div class="col-sm-4 offset-sm-4">
          <div class="btn-toolbar float-right">
            <!-- {{-- CREATE CUSTOMER --}} -->
            <b-button @click="$root.$emit('bv::show::modal', 'customerModal', $event.target)" variant="success" title="Create New Customer">
              CREATE
            </b-button>
          </div>
        </div>
      </div>

      <!-- Modal Customer Create -->
      <omd-modal :customers="customers" modal-type="create" @create-customer="customers.push($event)"></omd-modal>

      <!-- Search Bar -->
      <div class="row">
        <div class="col-12">
          <b-input-group class="w-50 m-auto">
            <b-form-input placeholder="Search for Customers" v-model="filter" class="rounded-pill searchbar">
            </b-form-input>
          </b-input-group>
        </div>
      </div>

      <!-- Customer List (Card Content)  -->
      <div class="row ">
        <div class="col">
          <b-table ref="table" :key="customer.id" show-empty :filter="filter" @filtered="on_filtered" id="customers-table" :sort-by.sync="sort_by" :sort-desc.sync="sort_desc" :items="customers" :fields="fields" :per-page="per_page" :current-page="current_page" responsive hover head-variant="light" class="text-center mt-4">
            <template v-slot:cell(#)="data">
              {{data.index+1}}
            </template>
            <template v-slot:cell(first_name)="data">
              {{data.item.first_name}} {{data.item.last_name}}
            </template>

            <template v-slot:cell(actions)="data">
              <div class="btn-group" role="group">

                <!-- {{-- EDIT CUSTOMER --}} -->
                <b-button @click="$root.$emit('bv::show::modal', data.item.first_name.replace(/\s/g, '')+data.item.id)" variant="primary" title="Edit Customer">
                  Edit
                </b-button>
              </div>

              <!--Edit Customer Modal-->
              <omd-modal modal-type="edit" :selected-customer="data.item" :customers="customers" @update-customer="data.item = $event" :key="data.item.id"></omd-modal>
            </template>
          </b-table>
        </div>
      </div>

      <hr>
      <!-- {{-- Card Footer --}} -->
      <div class="row">
        <div class="col-sm-4">
          <small class="text-muted"> Total Customers {{rows}} </small>
        </div>

        <div class="col-sm-4">
          <b-pagination class=" m-auto justify-content-center" pills :per-page="per_page" :total-rows="rows" v-model="current_page" aria-controls="#customers-table">
          </b-pagination>
        </div>
      </div>
    </div>
  </div>
</div>

<template id="customer-modal">
  <b-modal :id="customer.first_name.replace(/\s/g, '')+customer.id || 'customerModal'" title="Customer Modal" @hidden="resetModal" hide-footer>
    <b-alert id="alert" style="width: 100%;" :show="alert_sec" dismissible :variant="alert_color" @dismissed="alert_sec=0" @dismiss-count-down="countDown">
      {{alert_message}}
    </b-alert>
    <b-form @submit.prevent="onSubmit($v, $event)">
      <!-- first_name -->
      <b-form-group id="first-name-group" label="First Name" label-for="first-name">

        <b-form-input id="first-name" type="text" :value="customer.first_name" :state="first_nameValidate" @change="setFirstName($event)" required></b-form-input>

        <b-form-invalid-feedback :state="first_nameValidate">First Name is required and must be 3-5 character long.</b-form-invalid-feedback>
      </b-form-group>

      <!-- Last_name -->
      <b-form-group id="last-name-group" label="Last Name" label-for="last-name">
        <b-form-input id="last_name" :value="customer.last_name" :state="last_nameValidate" @change="setLastName($event)" type="text" required>
        </b-form-input>
        <b-form-invalid-feedback :state="last_nameValidate">Last Name is required and must be 3-5 character long.</b-form-invalid-feedback>
      </b-form-group>

      <!-- Email -->
      <b-form-group id="email-group" label="Email" label-for="email">
        <b-form-input id="email" :state="emailValidate" :value="customer.email" @change="setEmail($event)" type="email" required>
        </b-form-input>
        <b-form-invalid-feedback :state="emailValidate">Invalid Email.</b-form-invalid-feedback>
      </b-form-group>

      <hr style="margin-top: 2rem; margin-bottom: 1rem;">
      <div class="row justify-content-center">
        <b-button variant="success" class="mr-1" type="submit">Save Changes</b-button>
        <b-button variant="danger" class="ml-1" @click="$bvModal.hide(customer.first_name.replace(/\s/g, '')+customer.id || 'customerModal')">Cancel</b-button>
      </div>
    </b-form>

  </b-modal>
</template>

<script src="https://unpkg.com/vuelidate@0.7.5/dist/validators.min.js"></script>
<script src="https://unpkg.com/vuelidate@0.7.5/dist/vuelidate.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
👤Hiws

Leave a comment