@tykus
<template>
<section class="flex flex-col items-center">
<y-form>
<template #header>
{{ $t(title) }}
</template>
<template #default>
<y-image-uploader @image-selected="handleImageSelected">Image</y-image-uploader>
<y-input
v-model="branch.name"
object="branch"
id="name" type="text"
:error="errors?.name ? errors.name[0] : null"
>
{{ $t('branch.name') }}
</y-input>
<y-button @click.prevent="save()">{{ $t('action.save') }}</y-button>
<y-button @click.prevent="cancel()">{{ $t('action.cancel') }}</y-button>
</template>
</y-form>
</section>
</template>
<script setup>
const branch = reactive({});
const title = ref('branch.create');
// for better readability, I have omitted some lines from the component
const handleImageSelected = (image) => {
branch.imageToUpload = image;
};
async function loadBranch() {
const id = route.params.id ? parseInt(route.params.id) : null;
if (id) {
title.value = 'branch.edit';
const res = await branchService.show(id);
Object.assign(branch, res.data.data);
title.value = 'branch.edit';
} else {
branch.color = '#000000';
}
}
async function save() {
let res = null;
try {
if (branch.id) {
res = await branchService.update(branch);
} else {
res = await branchService.store(branch);
}
} catch (error) {
errors.value = error.response.data.errors;
}
if (res.status === 200 || res.status === 201) {
router.push({ name: 'dashboard' });
}
}
async function cancel() {
router.push({ name: 'dashboard' });
}
</script>
Here is the y image uploader component.
<template>
<fieldset class="space-y-1">
<label class="text-yggdra-light italic" :for="props.id">
<slot></slot>
</label>
<div
v-if="!fileToUpload"
class="transition-all h-20 flex justify-center items-center border border-yggdra-light rounded text-yggdra-light/70 hover:text-yggdra-light cursor-pointer text-4xl"
@click.prevent="$refs.fileUploader.click()"
>
<font-awesome-icon icon="fa-solid fa-image"></font-awesome-icon>
</div>
<input
ref="fileUploader"
v-show="false"
@change="handleImageChange($event)"
type="file"
accept="image/*"
>
<span v-if="props.error" class="text-sm text-red-500 italic">{{ $t(object+'.'+props.error) }}</span>
<div v-if="fileToUpload" class="relative">
<img class="w-full rounded" :src="fileUrl" alt="Preview image">
<div @click.prevent="removeFile()" class="absolute top-6 right-6 text-yggdra-red text-4xl cursor-pointer">
<font-awesome-icon icon="fa-regular fa-circle-xmark"></font-awesome-icon>
</div>
</div>
</fieldset>
</template>
<script setup>
import { ref } from 'vue';
const props = defineProps({
error: {
type: String,
},
});
const fileUploader = ref(null);
const fileToUpload = ref(null);
const fileUrl = ref(null);
const emit = defineEmits(['image-selected']);
const handleImageChange = (event) => {
const files = event.target.files;
if (!files.length) {
return;
}
fileToUpload.value = files[0];
if (fileToUpload.value && fileToUpload.value.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = (e) => {
fileUrl.value = e.target.result;
};
reader.readAsDataURL(fileToUpload.value);
} else {
fileUrl.value = null;
}
emit('image-selected', fileToUpload.value);
}
const removeFile = () => {
fileToUpload.value = null;
fileUrl.value = null;
}
</script>
And here is my service.
async update(branch) {
const res = await api.put('branches/'+branch.id, { ...branch }, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
useFlashStore().addMessage('success', 'branch_updated');
return res;
},
With api being.
const api = axios.create({
baseURL: url('api/v1/'),
});
I don't use the form markup to send the data, I retrieve the data via the branch reactive property to send them to the backend.