I've a list of objects that are loaded into a for-loop of child components. For each child, there is a button that allow me to remove the selected child.
The deleted child is deleted correctly from the list but, when rendered, only the last child is removed.
I don't know what I'm doing wrong.
Here is the code from the parent :
<tbody>
<route-form-row
v-for="(route, index) in form.current_routes"
:key="index"
:organizations="organizations"
:route="route"
:index="index"
:completed="form.completed"
@onUpdateOrganization="updateCurrentRouteOrganization"
@onUpdateNumberOfRoutes="updateCurrentRouteNumberOfRoutes"
@onRemoveRoute="removeCurrentRoute"
></route-form-row>
</tbody>
Here is how I remove the child item:
removeCurrentRoute(index) {
this.form.current_routes.splice(index, 1);
}
Here is the child component:
<template>
<tr>
<td>
<select v-model="organization" :class="['form-select', {'is-invalid': organizationError}]" @change.prevent="updateOrganization()">
<option value="">------</option>
<option v-for="org in organizations" :key="org.id" :value="org" :selected="organization.id === org.id">{{ org.name }}</option>
</select>
<div v-for="error of this.v$.organization.$errors" :key="error.$uid" :class="[{'invalid-feedback': organizationError}]">{{ error.$message }}</div>
</td>
<td class="col-2">
<input type="number" v-model="number_of_routes" :class="['form-control', {'is-invalid': numberOfRoutesError}]" step="1" min="0" @keyup="updateNumberOfRoutes">
<div v-for="error of this.v$.number_of_routes.$errors" :key="error.$uid" :class="[{'invalid-feedback': numberOfRoutesError}]">{{ error.$message }}</div>
</td>
<td class="text-end">
<div>
<a href="#" @click.prevent="removeRoute()">
<font-awesome-icon :icon="['far', 'trash-can']" />
</a>
</div>
</td>
</tr>
</template>
<script>
import { useVuelidate } from '@vuelidate/core'
import { requiredIf, helpers, minValue } from "@vuelidate/validators"
export default {
name: 'RouteFormRow',
props: {
index: Number,
route: Object,
organizations: Array,
completed: Boolean
},
setup() {
return { v$: useVuelidate() };
},
data() {
return {
organization: this.route.organization,
number_of_routes: this.route.number_of_routes,
}
},
validations() {
return {
organization: { required: helpers.withMessage('Ce champ est obligatoire', requiredIf(this.completed)) },
number_of_routes: {
required: helpers.withMessage('Ce champ est obligatoire', requiredIf(this.completed)),
minValue: helpers.withMessage('Assurez-vous que cette valeur est supérieure ou égale à 0.', minValue(0)),
}
}
},
computed: {
organizationError () {return this.v$.organization.$errors.length > 0},
numberOfRoutesError () {return this.v$.number_of_routes.$errors.length > 0},
},
methods: {
updateOrganization() {
this.$emit('onUpdateOrganization', this.index, this.organization);
},
updateNumberOfRoutes() {
this.$emit('onUpdateNumberOfRoutes', this.index, this.number_of_routes)
},
removeRoute() {
this.$emit('onRemoveRoute', this.index);
}
}
}
</script>
Here is the result when data is loaded and after I clicked on "delete" :
I found the problem.
As @EstusFlask mentioned, I should not use the index as key.
After further investigation, I replaced :key="index"
with :key="route"
.
Now it works fine.