Refactoring Vue template causes errors

ookma-kyi

Jan 9th, 2025 02:08 PM

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?

bobbyiliev

Jan 9th, 2025 11:44 PM

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 use defineEmits. 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 emitting selection-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 emit form.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!