I have a parent component 'DaftarKajian' and child component 'DaftarKajianMobile' and another child component named 'EditJadwalDialog'. What I have to tried is props the fetched data from 'DaftarKajianMobile' to 'EditKajianDialog'. My question is, how to display the props data in v-text-field on 'EditKajianDialog', then when I edit and submit the data, the variable of the data is changed too. For example formData.materi has value = "Vue-JS" then i want to edit it to "VueJS and Vuetify". This is the 'DaftarKajian'
<template>
<div v-if="mdAndDown">
<DaftarKajianMobile :dataKajianMobile="jadwalKajian" />
</div>
<div v-else>
<v-row class="mx-1">
<span class="tw-font-medium tw-text-xl">{{ route.meta.title }}</span>
</v-row>
<v-row class="mb-1 d-flex justify-space-between">
<v-col cols="5">
<v-text-field v-model="search" label="Search" prepend-inner-icon="mdi-magnify" variant="outlined" class="ml-2" hide-details></v-text-field>
</v-col>
<v-col class="d-flex justify-end align-center">
<v-btn @click="dialogAdd = !dialogAdd"><v-icon>mdi-plus</v-icon>Tambah</v-btn>
</v-col>
</v-row>
<!-- V-DATA-TABLE -->
<v-data-table :headers="(headers as any)" :items="jadwalKajian" :search="search" :loading="loading">
<template v-slot:item="{ item }">
<tr>
<td class="text-capitalize">{{ item.materi }}</td>
<td>{{ new Date(item.tanggal).formatDateID }}</td>
<td class="text-capitalize">{{ item.namaUstadz }}</td>
<v-tooltip text="Klik untuk copy" location="bottom">
<template v-slot:activator="{ props }">
<td v-bind="props" @click="copyText(item.tanggal, item.token, item.mulai, item.berakhir)" style="cursor: pointer">{{ item.token }}</td>
</template>
</v-tooltip>
<td>{{ item.mulai }}</td>
<td>{{ item.berakhir }}</td>
<td>
<v-tooltip text="Edit" location="bottom">
<template v-slot:activator="{ props }">
<v-icon v-bind="props" @click="openDialogEdit(item.id!)">mdi-pencil</v-icon>
</template>
</v-tooltip>
<v-tooltip text="Delete" location="bottom">
<template v-slot:activator="{ props }">
<v-icon v-bind="props" @click="openDialogDelete(item.id!)">mdi-delete</v-icon>
</template>
</v-tooltip>
</td>
</tr>
</template>
</v-data-table>
<!-- V-DATA-TABLE -->
<EditJadwalDialog :dataKajian="getKajianById!" :isShow="dialogEdit" @newData="handleNewEditData" @closeDialog="dialogEdit = false" />
<AddJadwalDialog :isShow="dialogAdd" @newData="handleNewAddData" @closeDialog="dialogAdd = false" />
<v-dialog v-model="dialogDelete" max-width="500px">
<v-card>
<v-card-title class="text-h5 text-center" style="white-space: normal">Apakah Anda yakin ingin menghapus Kajian/Event ini?</v-card-title>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue-darken-1" variant="text" @click="dialogDelete = false">Cancel</v-btn>
<v-btn color="red-darken-1" variant="text" @click="deleteConfirm()">OK</v-btn>
<v-spacer></v-spacer>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from "vue";
import { useAlertsStore } from "@/store";
import { useRoute } from "vue-router";
import { useDisplay } from "vuetify";
import { TGetKajianSchedule } from "@/types";
import { kajian } from "@/repository";
import AddJadwalDialog from "@/components/Aik/AddJadwalDialog.vue";
import EditJadwalDialog from "@/components/Aik/EditJadwalDialog.vue";
import DaftarKajianMobile from "@/components/Aik/DaftarKajianMobile.vue";
import "@/utils/date.extension";
const search = ref("");
const route = useRoute();
const alerts = useAlertsStore();
const loading = ref(false);
const dialogAdd = ref(false);
const dialogEdit = ref(false);
const dialogDelete = ref(false);
const { mdAndDown } = useDisplay();
const selectedId = ref<number>();
const jadwalKajian = ref<TGetKajianSchedule[]>([]);
const headers = ref([
{
align: "start",
key: "materi",
sortable: false,
title: "Judul Kajian",
},
{ align: "center", key: "tanggal", title: "Tanggal" },
{ align: "center", key: "namaUstadz", title: "Pemateri" },
{ align: "center", key: "token", title: "Token" },
{ align: "center", key: "mulai", title: "Mulai" },
{ align: "center", key: "berakhir", title: "Berakhir" },
{ align: "center", key: "actions", title: "Actions" },
]);
const fetchJadwalKajian = async () => {
loading.value = true;
try {
const result = await kajian.getJadwalKajian();
jadwalKajian.value = result;
} catch (error) {
if (error instanceof Error) alerts.showSnackbar(error.message, "error");
} finally {
loading.value = false;
}
};
onMounted(() => {
fetchJadwalKajian();
});
const handleNewAddData = (newData: any) => {
try {
console.log(newData);
jadwalKajian.value.push(newData);
} catch (error) {
if (error instanceof Error) alerts.showSnackbar(error.message, "error");
}
};
const handleNewEditData = (newData: any) => {
replaceNewData(newData.id, newData.formData);
};
const openDialogEdit = (id: number) => {
selectedId.value = id;
dialogEdit.value = !dialogEdit.value;
};
const openDialogDelete = (id: number) => {
selectedId.value = id;
dialogDelete.value = !dialogDelete.value;
};
const getKajianById = computed(() => {
return jadwalKajian.value.find((n) => n.id === selectedId.value);
});
const deleteConfirm = async () => {
try {
if (selectedId.value == null) return alerts.showSnackbar("Tidak ada Kajian/Event yang dipilih", "error");
await kajian.deleteJadwalKajian(selectedId.value);
const index = jadwalKajian.value.findIndex((n) => n.id === selectedId.value);
if (index !== -1) {
jadwalKajian.value.splice(index, 1);
alerts.showSnackbar("Kajian/Event berhasil dihapus", "success");
}
} catch (error) {
if (error instanceof Error) alerts.showSnackbar(error.message, "error");
} finally {
dialogDelete.value = false;
}
};
const replaceNewData = (id: number, formData: any) => {
const index = jadwalKajian.value.findIndex((n) => n.id === id);
if (index != -1) {
jadwalKajian.value[index].materi = formData.materi;
jadwalKajian.value[index].namaUstadz = formData.namaUstadz;
jadwalKajian.value[index].tanggal = formData.tanggal;
jadwalKajian.value[index].mulai = formData.mulai;
jadwalKajian.value[index].berakhir = formData.berakhir;
}
};
const copyText = (tanggal: string, token: string, mulai: string, berakhir: string) => {
try {
const formattedDate = new Date(tanggal).formatDateID;
navigator.clipboard.writeText(`
Assalamualaikum Wr Wb
Berikut adalah token untuk presensi kajian online pada hari ${formattedDate}. Token berlaku dari pukul ${mulai} WIB hingga ${berakhir} WIB.
Link: rsml.app/simpel/presensi-kajian-token
Token: ${token}
Terimakasih, Wassalamualaikum Wr Wb`);
alerts.showSnackbar("Token berhasil dicopy ke clipboard", "success");
} catch (error) {
alerts.showSnackbar("Gagal mengcopy token", "error");
}
};
</script>
<style lang="scss" scoped>
.text-capitalize {
text-transform: capitalize;
}
</style>
and this is the 'DaftarKajianMobile'
<template>
<div>
<v-row class="mx-1 d-flex justify-space-between">
<span class="text-h7">Daftar Kajian</span>
<v-btn variant="plain" class="pb-3 pr-0" @click="dialogAdd = !dialogAdd">Tambah</v-btn>
</v-row>
<v-expansion-panels variant="accordion" class="mt-4">
<v-expansion-panel v-for="item in dataKajianMobile" :key="item.id" class="mb-2">
<v-expansion-panel-title class="tw-bg-slate-200">
<p style="text-transform: capitalize">{{ item.materi }}</p>
</v-expansion-panel-title>
<v-expansion-panel-text class="mb-2">
<v-table>
<tbody class="text-title">
<tr>
<td class="px-2">Nama Ustadz</td>
<td class="px-0" style="text-transform: capitalize">: {{ item.namaUstadz }}</td>
</tr>
<tr>
<td class="px-2">Tanggal</td>
<td class="px-0">: {{ new Date(item.tanggal).formatDateID }}</td>
</tr>
<tr>
<td class="px-2">Token</td>
<td class="px-0">: {{ item.token }}</td>
</tr>
<tr>
<td class="px-2">Mulai</td>
<td class="px-0">: {{ item.mulai }}</td>
</tr>
<tr>
<td class="px-2">Berakhir</td>
<td class="px-0">: {{ item.berakhir }}</td>
</tr>
</tbody>
</v-table>
<div class="text-center">
<v-icon class="mx-2" @click="openDialogEdit(item.id!)">mdi-pencil</v-icon>
<v-icon class="mx-2" @click="openDialogDelete(item.id!)">mdi-delete</v-icon>
<v-icon class="mx-2" @click="copyText(item.tanggal, item.token, item.mulai, item.berakhir)">mdi-content-copy</v-icon>
</div>
</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>
</div>
<AddJadwalDialog :isShow="dialogAdd" @closeDialog="dialogAdd = false" />
<EditJadwalDialog :isShow="dialogEdit" :dataKajian="getKajianById!" @newData="handleNewEditData" @closeDialog="dialogEdit = false" />
<v-dialog v-model="dialogDelete" max-width="500px">
<v-card>
<v-card-title class="text-h5 text-center" style="white-space: normal">Apakah Anda yakin ingin menghapus Kajian/Event ini?</v-card-title>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue-darken-1" variant="text" @click="dialogDelete = false">Cancel</v-btn>
<v-btn color="red-darken-1" variant="text" @click="deleteConfirm()">OK</v-btn>
<v-spacer></v-spacer>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup lang="ts">
import { defineProps, ref, computed, withDefaults } from "vue";
import { TGetKajianSchedule } from "@/types";
import { kajian } from "@/repository";
import { useAlertsStore } from "@/store";
import AddJadwalDialog from "./AddJadwalDialog.vue";
import EditJadwalDialog from "./EditJadwalDialog.vue";
const alerts = useAlertsStore();
const dialogAdd = ref(false);
const dialogEdit = ref(false);
const dialogDelete = ref(false);
const selectedId = ref<number>();
const props = withDefaults(
defineProps<{
dataKajianMobile: TGetKajianSchedule[];
}>(),
{
dataKajianMobile: () => [],
}
);
const dataKajian = props.dataKajianMobile;
const copyText = (tanggal: string, token: string, mulai: string, berakhir: string) => {
try {
const formattedDate = new Date(tanggal).formatDateID;
navigator.clipboard.writeText(`Assalamualaikum Wr Wb
Berikut adalah token untuk presensi kajian online pada hari ${formattedDate}. Token berlaku dari pukul ${mulai} WIB hingga ${berakhir} WIB.
Link: rsml.app/simpel/presensi-kajian-token
Token: ${token}
Terimakasih, Wassalamualaikum Wr Wb`);
alerts.showSnackbar("Token berhasil dicopy ke clipboard", "success");
} catch (error) {
alerts.showSnackbar("Gagal mengcopy token", "error");
}
};
const openDialogEdit = (id: number) => {
selectedId.value = id;
dialogEdit.value = !dialogEdit.value;
};
const getKajianById = computed(() => {
return dataKajian.find((n) => n.id === selectedId.value);
});
const handleNewEditData = (newData: any) => {
replaceNewData(newData.id, newData.formData);
};
const replaceNewData = (id: number, formData: any) => {
const index = dataKajian.findIndex((n) => n.id === id);
if (index != -1) {
dataKajian[index].materi = formData.materi;
dataKajian[index].namaUstadz = formData.namaUstadz;
dataKajian[index].tanggal = formData.tanggal;
dataKajian[index].mulai = formData.mulai;
dataKajian[index].berakhir = formData.berakhir;
}
};
const openDialogDelete = (id: number) => {
selectedId.value = id;
dialogDelete.value = !dialogDelete.value;
};
const deleteConfirm = async () => {
try {
if (selectedId.value == null) return alerts.showSnackbar("Tidak ada Kajian/Event yang dipilih", "error");
await kajian.deleteJadwalKajian(selectedId.value);
const index = dataKajian.findIndex((n) => n.id === selectedId.value);
if (index !== -1) {
dataKajian.splice(index, 1);
alerts.showSnackbar("Kajian/Event berhasil dihapus", "success");
}
} catch (error) {
if (error instanceof Error) alerts.showSnackbar(error.message, "error");
} finally {
dialogDelete.value = false;
}
};
</script>
<style lang="scss" scoped>
:deep(.v-expansion-panel-text__wrapper) {
padding: 0%;
}
.text-title {
font-size: calc(6px + 2vw);
}
</style>
and this is the 'EditKajianDialog'
<template>
<v-dialog :model-value="props.isShow" max-width="700">
<v-card prepend-icon="mdi-calendar" title="Edit Jadwal Kajian">
<v-card-text>
<v-row dense>
<v-col cols="12" md="6" sm="6">
<v-text-field
label="Judul/Materi Kajian"
v-model="formData.materi"
:input-classes="{ capitalize: true }"
style="text-transform: capitalize"
></v-text-field>
</v-col>
<v-col cols="12" md="6" sm="6">
<v-text-field label="Nama Ustadz" v-model="formData.namaUstadz" :input-classes="{ capitalize: true }"></v-text-field>
</v-col>
<v-col cols="12" md="4" sm="6">
<v-menu v-model="showPicker" :close-on-content-click="false">
<template v-slot:activator="{ props: activatorProps }">
<v-text-field v-bind="activatorProps" label="Tanggal Kajian" readonly :model-value="formattedDate"></v-text-field>
</template>
<v-date-picker v-model="selectedDate" @update:model-value="showPicker = false"></v-date-picker>
</v-menu>
</v-col>
<v-col cols="12" md="4" sm="6">
<v-text-field
type="time"
step="1"
min="00:00:00"
max="23:59:59"
label="Masa Berlaku Token (Mulai)"
v-model="formData.mulai"
></v-text-field>
</v-col>
<v-col cols="12" md="4" sm="6">
<v-text-field
type="time"
step="1"
min="00:00:00"
max="23:59:59"
label="Masa Berlaku Token (Berakhir)"
v-model="formData.berakhir"
></v-text-field>
</v-col>
</v-row>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="error" text="Close" variant="plain" @click="$emit('closeDialog')"></v-btn>
<v-btn color="success" text="Submit" variant="tonal" @click="submit"></v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup lang="ts">
import { reactive, ref, watch, computed } from "vue";
import { kajian } from "@/repository";
import { TGetKajianSchedule } from "@/types";
import { useAlertsStore } from "@/store";
const props = defineProps<{
isShow: boolean;
dataKajian: TGetKajianSchedule;
}>();
const emits = defineEmits(["newData", "closeDialog"]);
const alerts = useAlertsStore();
const showPicker = ref(false);
const selectedDate = ref(new Date());
const formData = reactive({
tanggal: "",
materi: "",
namaUstadz: "",
mulai: "",
berakhir: "",
token: "",
});
const submit = async () => {
console.log(formData);
try {
if (!formData.namaUstadz || !formData.materi || !formData.mulai || !formData.berakhir)
return alerts.showSnackbar("Mohon isi semua input dengan lengkap!", "error");
console.log(formData);
await kajian.putJadwalKajian(props.dataKajian!.id!, formData);
const newData = { id: props.dataKajian!.id, formData };
alerts.showSnackbar("Berhasil mengubah!", "success");
emits("newData", newData);
emits("closeDialog");
} catch (error) {
if (error instanceof Error) alerts.showSnackbar(error.message, "error");
}
};
const showData = () => {
console.log(props.dataKajian);
if (props.dataKajian) {
formData.tanggal = props.dataKajian.tanggal;
formData.materi = props.dataKajian.materi;
formData.namaUstadz = props.dataKajian.namaUstadz;
formData.mulai = props.dataKajian.mulai;
formData.berakhir = props.dataKajian.berakhir;
formData.token = props.dataKajian.token;
}
};
watch(
() => props.isShow,
(val) => {
if (val) {
showData();
}
}
);
const formattedDate = computed(() => {
return new Date(formData.tanggal).formatDateID;
});
watch(selectedDate, (newValue) => {
formData.tanggal = newValue.toISOStringOffset();
});
</script>
<style lang="scss" scoped>
.capitalize {
text-transform: capitalize;
}
</style>
Sorry for my bad English