I’m working on a VUE app. Part of the app is to show a table (team) with information from the backend (by axios / django rest_framework). This all works.
When I click "nieuwe categorie" the modal opens with a form. Onsubmit, the function "submitHandler" is used. However, in that function I can’t get the form data? Which I need to "POST" with axios to the backend….
I’ve tried v-model on the modal form, but gives a reference error.
Anyway, any help would be appreciated.
The modal component:
<script setup>
import { defineProps, defineEmits, ref } from "vue";
import { onClickOutside } from '@vueuse/core'
const offset = 6
const props = defineProps({
isOpen: Boolean,
isTest: String,
Categorie: String,
modalAction: String,
});
const emit = defineEmits(["modal-close"]);
const target = ref(null)
onClickOutside(target, () => emit('modal-close'))
</script>
<template>
<div v-if="isOpen" class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container" ref="target">
<div class="modal-header py-5">
<slot name="header">
<div v-if="modalAction === 'new'">
<h1>Nieuwe Categorie</h1>
</div>
<div v-else-if="modalAction === 'edit'"> {{ Categorie }}</div>
</slot>
</div>
<div class="modal-body">
<slot name="content">
<div class="mx-2" v-if="modalAction === 'new'">
<form class="w-full" @submit.prevent="submitHandler">
<div class="flex -mx-2">
<div class="items-center border-b border-teal-500 py-2 px-2">
<label
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
for="grid-first-name">
Categorie
</label>
<select
class="appearance-none bg-transparent border-none w-full text-gray-700 mr-3 py-1 px-2 leading-tight focus:outline-none"
placeholder="1:1" aria-label="">
<option v-for="n in 14">Onder-{{ n + offset }}
</option>
</select>
</div>
</div>
<div class="flex -mx-2">
<div class="w-full items-center border-b border-teal-500 py-2 px-2">
<label
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
for="grid-first-name">
T/M geboortejaar
</label>
<select
class="appearance-none bg-transparent border-none w-full text-gray-700 mr-3 py-1 px-2 leading-tight focus:outline-none"
placeholder="1:1" aria-label="">
<option v-for="n in 14">{{ n + 2003 }}</option>
</select>
</div>
<div class="w-10"></div>
<div class="w-full items-center border-b border-teal-500 py-2 px-2">
<label
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
for="grid-first-name">
Aantal veldspelers
</label>
<select
class="appearance-none bg-transparent border-none w-full text-gray-700 mr-3 py-1 px-2 leading-tight focus:outline-none"
placeholder="1:1" aria-label="">
<option>4</option>
<option>6</option>
<option>8</option>
<option>11</option>
</select>
</div>
</div>
<div class="flex -mx-2">
<div class="items-center w-full border-b border-teal-500 py-2 px-2">
<label
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
for="grid-first-name">
Veldomvang
</label>
<select
class="appearance-none bg-transparent border-none w-full text-gray-700 mr-3 py-1 px-2 leading-tight focus:outline-none"
type="select" placeholder="1:1" aria-label="veld">
<option>1:8</option>
<option>1:4</option>
<option>1:2</option>
<option>1:1</option>
</select>
</div>
<div class="w-10"></div>
<div class="items-center w-full border-b border-teal-500 py-2 px-2">
<label
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
for="grid-first-name">
Wedstrijdduur
</label>
<select
class="appearance-none bg-transparent border-none w-full text-gray-700 mr-3 py-1 px-2 leading-tight focus:outline-none"
placeholder="1:1" aria-label="">
<option>12</option>
<option>2x20 (na 10min time-out)</option>
<option>2x25 (na 12,5min time-out)</option>
<option>2x30 (na 15min time-out)</option>
<option>2x30</option>
<option>2x35</option>
<option>2x40</option>
<option>2x45</option>
</select>
</div>
</div>
<div class="flex -mx-2">
<div class="w-full items-center border-b border-teal-500 py-2 px-2">
<label
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
for="grid-first-name">
Dispensatie
</label>
<select
class="appearance-none bg-transparent border-none w-full text-gray-700 mr-3 py-1 px-2 leading-tight focus:outline-none"
placeholder="1:1" aria-label="">
<option v-for="n in 3">{{ n }}</option>
</select>
</div>
<div class="w-10"></div>
<div class="w-full items-center border-b border-teal-500 py-2 px-2">
<label
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
for="grid-first-name">
Actief binnen club
</label>
<input
class="appearance-none bg-transparent border-blue text-gray-700 mr-3 py-1 px-2 leading-tight focus:outline-none"
type="checkbox" placeholder="1" aria-label="dispensatie">
</input>
</div>
</div>
</form>
</div>
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
<div>
<!--<button @click.stop="emit('modal-close')">Submit</button>-->
<button>Submit</button>
</div>
</slot>
</div>
</div>
</div>
</div>
</template>
<script>
</script>
<style scoped>
.modal-mask {
position: fixed;
z-index: 9998;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-container {
width: 800px;
margin: 150px auto;
padding: 20px 30px;
background-color: #fff;
border-radius: 2px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
}
</style>
and the view:
<script setup>
import { ref } from "vue";
import ModalComponent from "../components/ModalComponent.vue";
import axios from 'axios'
const isModalOpened = ref(false);
const test = ref('')
const editCategorie = ref('')
const modalAction = ref('new')
const form = ref([])
const openModal = (cat, action) => {
if (!action) {
console.log('action: ', action)
action = 'new'
}
modalAction.value = action
editCategorie.value = cat.categorie
isModalOpened.value = true;
};
const closeModal = () => {
isModalOpened.value = false;
};
const submitHandler = () => {
//here I should be able to get the form data
console.log('data:', this.form) //this gives an error 'form not defined'
}
</script>
<template>
<div class="p-2 pt-20 sm:ml-64">
<div class="p-4 border-2 border-gray-200 border-dashed bg-gray-200 rounded-lg dark:border-gray-700 mt-14">
<!-- CRUD table goes here-->
<!-- Start block -->
<section class="bg-gray-50 dark:bg-gray-900 p-3 sm:p-5 antialiased">
<div class="mx-auto max-w-screen-xl px-4 lg:px-12">
<!-- Start coding here -->
<div class="bg-white dark:bg-gray-800 relative shadow-md sm:rounded-lg overflow-hidden">
<div
class="flex flex-col md:flex-row items-center justify-between space-y-3 md:space-y-0 md:space-x-4 p-4">
</div>
<div class="overflow-x-auto">
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<thead
class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th scope="col" class="px-4 py-3">Actief binnen club</th>
<th scope="col" class="px-4 py-3">Categorie</th>
<th scope="col" class="px-4 py-3">t/m geboortejaar</th>
<th scope="col" class="px-4 py-3">Max. aantal veldspelers</th>
<th scope="col" class="px-4 py-3">Veldomvang</th>
<th scope="col" class="px-4 py-3">Wedstrijdduur</th>
<th scope="col" class="px-4 py-3">Dispensatie</th>
<th scope="col" class="px-4 py-3">Wijzig</th>
</tr>
</thead>
<tbody v-for="cat in teamcats">
<tr class="border-b dark:border-gray-700">
<td class="px-4 py-3">
<input v-model="cat.actief" type="checkbox" />
</td>
<th scope="row"
class="px-4 py-3 font-medium text-gray-900 whitespace-nowrap dark:text-white">
{{ cat.categorie }}</th>
<td class="px-4 py-3">{{ cat.geboortejaar }}</td>
<td class="px-4 py-3">{{ cat.veldspelers }}</td>
<td class="px-4 py-3 max-w-[12rem] truncate">{{ cat.veldomvang }}</td>
<td class="px-4 py-3">{{ cat.wedstrijdduur }}</td>
<td class="px-4 py-3">{{ cat.dispensatie }}</td>
<td class="px-4 py-3"><button class="bg-blue-300"
@click="openModal(cat, mode = 'edit')">Wijzig</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="flex flex-col italic md:flex-row justify-between items-start md:items-center space-y-3 md:space-y-0 p-4"
aria-label="Table navigation">
<span class="text-sm font-normal text-gray-500 dark:text-gray-400">
Datum laatste wijziging:
<span class="font-semibold text-gray-900 dark:text-white">22-03-2024</span>
</span>
<div>
<!-- Modal toggle -->
<div>
<!--<button @click="openModal">Toevoegen Categorie</button>-->
<button @click="openModal">Toevoegen Categorie</button>
</div>
</div>
</div>
</div>
<!-- modal-->
<ModalComponent :isOpen="isModalOpened" :modalAction="modalAction" :Categorie="editCategorie"
@modal-close="closeModal" @submit="submitHandler" name="first-modal">
<template #header></template>
<template #content></template>
<template #footer><button @click="submitHandler">submit</button></template>
</ModalComponent>
<!-- modal-->
</div>
</section>
<!-- End block -->
<!-- END OF CRUD Table-->
</div>
</div>
</template>
<script>
import axios from 'axios'
import TestView from "./TestView.vue";
export default {
data() {
return {
teamcats: [],
}
},
mounted() {
this.getTeamCats()
},
methods: {
getTeamCats() {
axios
.get('/api/team/view')
.then(response => {
console.log(response.data)
this.teamcats = response.data
})
.catch(error => {
console.log('error', error)
})
},
}
}
</script>
2
Answers
with the support of @wbiller (THANKS SO MUCH!)
and some logic thinking...data was 'nothing' the final solution was to create a reactive data object:
and in the modal, the correct data binding:
Now I see the data in the submithandler: {categorie: 'Onder-14'}
You can pass the data using emit to the table component
In the modal component, define another emit. For that change this line
const emit = defineEmits(["modal-close"]);
to this here
const emit = defineEmits(["modal-close", "submit"]);
.Then define a function to emit this event and the data. Currently, you don’t have the data model in your modal, you need to create this first and bind it with v-model to the controls.
I saw you have overwritten the footer slot, either you undo that and call the submit function from the existing button, or you provide the submit function as a scoped slot (v-bind on slot). Then you can also use it in your table.
In your table make the submitHandler function accent one parameter, that will be the data.
If you want to be in control of the submit button, the footer of the modal in the table component must look like this: