Search code examples
javascriptloopsvue.jsbootstrap-modalbootstrap-vue

Delete button spawning multiple confirm modals in vue-bootstrap>


Looking to solve this problem with having a unique ref to the modal in this loop, tried giving the ref a grade.id at the end but wouldn't work with String interpolation with the b-modal ref, also tried :ref.

This is the code in the vue file, and below that a screenshot of what it looks like, when I press to delete a grade then it shows all of the modals as the modal is in a loop.

 <template>
  <div>
    <b-form @submit="onSubmit" @reset="onReset">
      <b-row>
        <b-col class="underline">Add Grades</b-col>
      </b-row>
      <br />
      <b-row>
        <b-col>
          <b-form-group
            inline
            label-cols-sm="4"
            label-cols-lg="3"
            id="input-group-1"
            label="Select Farm:"
          >
            <b-form-select v-model="form.farm" :options="farms" required />
          </b-form-group>
        </b-col>
      </b-row>
      <b-row class="my-1">
        <b-col>
          <b-form-group label-cols-sm="4" label-cols-lg="3" label="name:">
            <b-form-input
              v-model="form.gradeName"
              size="sm"
              placeholder="name"
            ></b-form-input>
          </b-form-group>
        </b-col>
      </b-row>
      <b-row class="my-1">
        <b-col>
          <b-form-group
            label-cols-sm="4"
            label-cols-lg="3"
            label="min weight(gms):"
          >
            <b-form-input
              v-model="form.gradeMinWeight"
              size="sm"
              placeholder="minimum"
            ></b-form-input>
          </b-form-group>
        </b-col>
      </b-row>
      <b-row class="my-1">
        <b-col>
          <b-form-group
            label-cols-sm="4"
            label-cols-lg="3"
            label="max weight(gms):"
          >
            <b-form-input
              v-model="form.gradeMaxWeight"
              size="sm"
              placeholder="maximum"
            ></b-form-input>
          </b-form-group>
        </b-col>
      </b-row>
      <b-row class="my-1">
        <b-col>
          <b-form-group label-cols-sm="4" label-cols-lg="3" label="shape:">
            <b-form-select v-model="form.gradeShape">
              <option value="rounded">rounded</option>
              <option value="flat">flat</option>
              <option value="curve">curve</option>
            </b-form-select>
          </b-form-group>
        </b-col>
        <b-col>
          <b-form-group label-cols-sm="4" label-cols-lg="3" label="dimension:">
            <b-form-select v-model="form.gradeDimension">
              <option value="rounded">seeds</option>
              <option value="rounded">small</option>
              <option value="flat">medium</option>
              <option value="curve">large</option>
              <option value="curve">extra large</option>
              <option value="curve">jumbo</option>
            </b-form-select>
          </b-form-group>
        </b-col>
      </b-row>
      <b-row>
        <b-col>
          <b-button type="submit" variant="primary" block>Add</b-button>
        </b-col>
        <b-col>
          <b-button type="reset" variant="secondary" block>Cancel</b-button>
        </b-col>
      </b-row>
    </b-form>

    <br />
    <b-row>
      <b-col class="underline">My Grades</b-col>
    </b-row>
    <br />
    <div v-if="grades.length > 0">
      <b-card-group class="mb-4" v-for="grade in grades" :key="grade.id">
        <b-card :title="getTitle(grade)">
          <b-card-text class="list-card">
            {{ getProperties(grade) }}

            <span style="float: right">
              <b-button
                class="mr-1"
                type="reset"
                variant="danger"
                @click="showModal"
                size="sm"
              >
                <!-- Fix css to icon middle -->
                <b-icon icon="trash-fill" size="is-small"></b-icon>
              </b-button>

              <b-modal
                scrollable
                id="modal-delete-grade"
                ref=' `modal-delete-grade'
                centered
                @ok="actionDelete(grade.id)"
                class="d-block text-center"
                title="Confirm Delete Grade"
              >
                <p>
                  Confirm that you are about to delete grade
                  {{ getTitle(grade) }}
                </p>
              </b-modal>
            </span>
          </b-card-text>
        </b-card>
      </b-card-group>
    </div>
  </div>
</template>

<script>
import makeToast from "../../helper/toast";
import farmService from "../../service/FarmService";
import { getUser } from "@/helper/storage";
import userService from "@/service/UserService";
import DashEdit from "@/views/DashEdit";
import { contentMixin } from "@/mixins/contentMixin";
import { setupMixin } from "@/mixins/setupMixin";

export default {
  name: "AddSetupGrade",
  mixins: [contentMixin, setupMixin],
  data() {
    return {
      farms: [],
      grades: [],
      form: {
        farm: null,
        gradeName: null,
        gradeMinWeight: null,
        gradeMaxWeight: null,
        gradeShape: null,
        gradeDimension: null,
      },
    };
  },
  mounted() {
    this.getUserFarm();
    this.getGrades();
  },
  created() {
    this.getGrades();
  },
  methods: {
    async onSubmit(evt) {
      try {
        evt.preventDefault();
        let response = await farmService.addGrades({
          name: this.form.gradeName,
          farmId: this.form.farm.id,
          properties: {
            minWeight: this.form.gradeMinWeight,
            maxWeight: this.form.gradeMaxWeight,
            units: "gms",
            shape: this.form.gradeShape,
            dimension: this.form.gradeDimension,
          },
        });

        if (response.status === 200) {
          makeToast.call(
            this,
            "Success",
            "Successfully created grade",
            "success"
          );

          this.onReset();
          this.getGrades();
        }
      } catch (e) {
        console.log(`Error in creating grade. ${e}`);
        makeToast.call(this, "Error", "Error adding grade", "danger");
      }
    },
    onReset() {
      this.form.gradeName = null;
      this.form.gradeMinWeight = null;
      this.form.gradeMaxWeight = null;
      this.form.gradeShape = null;
      this.form.gradeDimension = null;
    },

    async getUserFarm() {
      try {
        let { id: userId } = getUser();
        let response = await userService.getFarmsOfUser(userId);
        let farmList = response.data.data;
        this.farms = farmList.map((each) => {
          return {
            value: { id: each.id, name: each.name },
            text: each.name,
          };
        });
        this.farms.push({ value: null, text: "Select Farm" });
      } catch (e) {
        console.log(`Error: ${e}`);
      }
    },

    getTitle(grade) {
      if (grade) {
        return grade.name + " - " + grade.alias;
      }
    },
    showModal(gradeId) {
      this.$bvModal.show("modal-delete-grade");
    },
    keyValString(obj) {
      return Object.entries(obj).reduce((str, [key, value], index) => {
        if (value) {
          if (str) str += ", ";
          str += `${key}: ${value}`;
        }
        return str;
      }, "");
    },

    getProperties(grade) {
      return (
        (grade && grade.properties && this.keyValString(grade.properties)) ||
        "No properties on grade."
      );
    },

    async getGrades() {
      try {
        let response = await farmService.getGrades();
        this.grades = response.data.data;
      } catch (e) {
        console.log(e);
      }
    },

    async actionDelete(id) {
      try {
        let response = await farmService.deleteGrade(id);
        this.$bvModal.hide("modal-delete-grade");
        this.getGrades();
      } catch (e) {
        console.log(e.response.data);
      }
    },
  },
};
</script>

<style scoped>
.underline {
  border-bottom: 2px solid currentColor;
  border-bottom-color: #d3d3d3;
  font-weight: bold;
  font-size: 16px;
}
.list-card {
  font-family: Open Sans;
  font-size: 14px;
  line-height: (48 / 24);
  letter-spacing: -0.046em;
  color: black;
}
.card-title {
  font-size: 1em;
  font-family: Open Sans;
}
</style>

enter image description here


Solution

  • You could move the modal outside of the loop. To pass the data of the clicked grade, simply save it to your data for example as "activeGrade". In your modal you display the data of "activeGrade". Now you can open the (same) modal with the clicked grade specific data.

    I simplified your code a bit to show only the asked issue:

    <template>
      <div class="hello">
        <b-card-group class="mb-4" v-for="grade in grades" :key="grade.id">
          <b-card :title="grade.title">
            <b-card-text class="list-card">
              Some Text (grade properties)
    
              <span style="float: right">
                <b-button
                  class="mr-1"
                  type="reset"
                  variant="danger"
                  @click="showModal(grade)"
                  size="sm">
                  <!-- Fix css to icon middle -->
                  <b-icon icon="trash-fill" size="is-small"></b-icon>
                </b-button>
              </span>
            </b-card-text>
          </b-card>
        </b-card-group>
    
        <b-modal v-if="activeGrade"
                scrollable
                id="modal-delete-grade"
                centered
                @ok="actionDelete(activeGrade.id)"
                class="d-block text-center"
                title="Confirm Delete Grade">
          <p>
            Confirm that you are about to delete grade
            {{ activeGrade.title }}
          </p>
        </b-modal>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          grades: [
            { id: 1, title: 'Grate 1' },
            { id: 2, title: 'Grate 2' },
            { id: 3, title: 'Grate 3' },
          ],
          activeGrade: null
        }
      },
      methods: {
        actionDelete(gradeId) {
          console.log('delete ' + gradeId)
        },
        showModal(grade) {
          this.activeGrade = grade
    
          this.$nextTick(() => {
            this.$bvModal.show("modal-delete-grade");
          })
        }
      }
    }
    </script>