Refactoring Vue template causes errors
I am trying to pass values from a child vue to a parent and it doesn't work. I tried using console.log
to debug and it doesn't appear in my Firefox web developer tools console at all. Based on this I have determined that the child values are not being passed to the parent:
Duels/Partials/OffensiveMovesFieldset.vue
<script setup>
import InputError from "@/Components/InputError.vue";
import InputLabel from "@/Components/InputLabel.vue";
import {useForm} from "@inertiajs/vue3";
const props = defineProps(['offensive_moves']);
const form = useForm({
offensive_moves: {
offensive_move1: props.offensive_moves[0].id,
offensive_move2: props.offensive_moves[0].id,
offensive_move3: props.offensive_moves[0].id,
offensive_move4: props.offensive_moves[0].id,
offensive_move5: props.offensive_moves[0].id,
offensive_move6: props.offensive_moves[0].id,
}
});
const emitSelection = () => {
this.$emit('selection-changed', this.offensive_moves);
}
</script>
<template>
<fieldset class="border border-gray-300 p-2 rounded">
<legend class="text-lg font-bold">Offensive Moves</legend>
<InputLabel for="offensive_move1" value="1st Offensive Move" />
<select v-model="form.offensive_move1" @change="emitSelection" id="offensive_move1" class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option v-for="move in offensive_moves" :key="move.id" :value="move.id">
{{ move.name }}
</option>
</select>
<InputError :message="form.errors.offensive_move1" class="mt-1" />
<InputLabel for="offensive_move2" value="2nd Offensive Move" />
<select v-model="form.offensive_move2" @change="emitSelection" id="offensive_move2" class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option v-for="move in offensive_moves" :key="move.id" :value="move.id">
{{ move.name }}
</option>
</select>
<InputError :message="form.errors.offensive_move2" class="mt-1" />
<InputLabel for="offensive_move3" value="3rd Offensive Move" />
<select v-model="form.offensive_move3" @change="emitSelection" id="offensive_move3" class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option v-for="move in offensive_moves" :key="move.id" :value="move.id">
{{ move.name }}
</option>
</select>
<InputError :message="form.errors.offensive_move3" class="mt-1" />
<InputLabel for="offensive_move4" value="4th Offensive Move" />
<select v-model="form.offensive_move4" @change="emitSelection" id="offensive_move4" class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option v-for="move in offensive_moves" :key="move.id" :value="move.id">
{{ move.name }}
</option>
</select>
<InputError :message="form.errors.offensive_move4" class="mt-1" />
<InputLabel for="offensive_move5" value="5th Offensive Move" />
<select v-model="form.offensive_move5" @change="emitSelection" id="offensive_move5" class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option v-for="move in offensive_moves" :key="move.id" :value="move.id">
{{ move.name }}
</option>
</select>
<InputError :message="form.errors.offensive_move5" class="mt-1" />
<InputLabel for="offensive_move6" value="6th Offensive Move" />
<select v-model="form.offensive_move6" @change="emitSelection" id="offensive_move6" class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option v-for="move in offensive_moves" :key="move.id" :value="move.id">
{{ move.name }}
</option>
</select>
<InputError :message="form.errors.offensive_move6" class="mt-1" />
</fieldset>
</template>
Duels/Ceate.vue
<script setup>
import AppLayout from "@/Layouts/AppLayout.vue";
import Container from "@/Components/Container.vue";
import InputError from "@/Components/InputError.vue";
import InputLabel from "@/Components/InputLabel.vue";
import TextInput from "@/Components/TextInput.vue";
import {useForm} from "@inertiajs/vue3";
import PrimaryButton from "@/Components/PrimaryButton.vue";
import OffensiveMovesFieldset from "@/Pages/Duels/Partials/OffensiveMovesFieldset.vue";
const props = defineProps(['offensive_moves', 'defensive_moves']);
const form = useForm({
opponent: '',
offensive_move1: number,
offensive_move2: number,
offensive_move3: number,
offensive_move4: number,
offensive_move5: number,
offensive_move6: number,
defensive_move1: props.defensive_moves[0].id,
defensive_move2: props.defensive_moves[0].id,
defensive_move3: props.defensive_moves[0].id,
defensive_move4: props.defensive_moves[0].id,
defensive_move5: props.defensive_moves[0].id,
defensive_move6: props.defensive_moves[0].id,
});
const submit = () => {
form.transform(data => ({
...data,
})).post(route('duels.store'));
};
const handleSelectionChange = (offensive_moves) => {
console.log("Offensive Move 1" . offensive_moves.offensive_move1);
form.offensive_move1 = offensive_moves.offensive_move1
form.offensive_move2 = offensive_moves.offensive_move2
form.offensive_move3 = offensive_moves.offensive_move3
form.offensive_move4 = offensive_moves.offensive_move4
form.offensive_move5 = offensive_moves.offensive_move5
form.offensive_move6 = offensive_moves.offensive_move6
}
</script>
<template>
<AppLayout title="Create Duel">
<div class="grid row mt-5">
<div class="col-span-6 m-auto">
<div class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
<container>
<h1 class="text-center mb-3">
<i aria-hidden="true"></i> Issue Challenge
</h1>
<form @submit.prevent="submit">
<fieldset class="border border-gray-300 p-2 rounded">
<legend class="text-lg font-bold">Opponent</legend>
<InputLabel for="opponent" value="Opponent to issue challenge" />
<TextInput
id="name"
v-model="form.opponent"
type="text"
class="mt-1 block w-full"
minlength="4"
maxlength="15"
required
autofocus
autocomplete="name"
/>
<InputError class="mt-2" :message="form.errors.opponent" />
</fieldset>
<OffensiveMovesFieldset @sinput="handleSelectionChange" :offensive_moves="props.offensive_moves" />
<fieldset class="border border-gray-300 p-2 rounded">
<legend class="text-lg font-bold">Defensive Moves</legend>
<InputLabel for="defensive_move1" value="1st Defensive Move" />
<select v-model="form.defensive_move1" id="defensive_move1" class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option v-for="move in defensive_moves" :key="move.id" :value="move.id">
{{ move.name }}
</option>
</select>
<InputError :message="form.errors.defensive_move1" class="mt-1" />
<InputLabel for="defensive_move2" value="2nd Defensive Move" />
<select v-model="form.defensive_move2" id="defensive_move2" class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option v-for="move in defensive_moves" :key="move.id" :value="move.id">
{{ move.name }}
</option>
</select>
<InputError :message="form.errors.defenseive_move2" class="mt-1" />
<InputLabel for="defensive_move3" value="3rd Defensive Move" />
<select v-model="form.defensive_move3" id="defensive_move3" class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option v-for="move in defensive_moves" :key="move.id" :value="move.id">
{{ move.name }}
</option>
</select>
<InputError :message="form.errors.defensive_move3" class="mt-1" />
<InputLabel for="defensive_move4" value="4th Defensive Move" />
<select v-model="form.defensive_move4" id="defensive_move4" class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option v-for="move in defensive_moves" :key="move.id" :value="move.id">
{{ move.name }}
</option>
</select>
<InputError :message="form.errors.defensive_move4" class="mt-1" />
<InputLabel for="defensive_move5" value="5th Defensive Move" />
<select v-model="form.defensive_move5" id="defensive_move5" class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option v-for="move in defensive_moves" :key="move.id" :value="move.id">
{{ move.name }}
</option>
</select>
<InputError :message="form.errors.defensive_move5" class="mt-1" />
<InputLabel for="defensive_move6" value="6th Defensive Move" />
<select v-model="form.defensive_move6" id="defensive_move6" class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option v-for="move in defensive_moves" :key="move.id" :value="move.id">
{{ move.name }}
</option>
</select>
<InputError :message="form.errors.defensive_move6" class="mt-1" />
</fieldset>
<PrimaryButton class="ms-4" :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
Submit
</PrimaryButton>
</form>
</container>
</div>
</div>
</div>
</AppLayout>
</template>
Any ideas?
Hi there,
After a quick glance and an LLM check, as far as I can tell there are several problems that need to be addressed:
- The emit syntax in the child component (OffensiveMovesFieldset.vue) is incorrect. In the setup script, you can't use
this.$emit
. Instead, you need to define and usedefineEmits
. Here's how to fix it:
// In OffensiveMovesFieldset.vue
const emit = defineEmits(['selection-changed']);
const emitSelection = () => {
emit('selection-changed', form.offensive_moves);
}
- The event name in the parent component (Create.vue) doesn't match the template. You're using
@sinput
but emittingselection-changed
. The names should match:
<!-- In Create.vue, change this: -->
<OffensiveMovesFieldset @selection-changed="handleSelectionChange" :offensive_moves="props.offensive_moves" />
- There's a syntax error in the console.log statement in handleSelectionChange. The string concatenation is using a dot instead of a plus:
// Change this:
console.log("Offensive Move 1" . offensive_moves.offensive_move1);
// To this:
console.log("Offensive Move 1: " + offensive_moves.offensive_move1);
- In the parent form initialization,
number
is used as a value which will cause an error:
const form = useForm({
opponent: '',
// Change this:
offensive_move1: number,
// To this:
offensive_move1: props.offensive_moves[0].id,
// Do the same for moves 2-6
});
- The emitted value structure doesn't match what the parent expects. The child is emitting
this.offensive_moves
but should emitform.offensive_moves
:
Here's the corrected child component emitSelection function:
const emitSelection = () => {
emit('selection-changed', form.offensive_moves);
}
These changes should get the parent-child communication working properly. The console.log statements should now appear in the Firefox developer tools, and the values should be properly passed from child to parent.
Let me know if this works!