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>
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>