In my app I have a form, in the ManageCards component, that conatins a child component ImageUpload, that emits and imagefile and its local URL to the parent:
<form class="mb-3" >
<div class="form-group">
<h4>{{card.description}}</h4>
<textarea class="form-control" :placeholder="card.description" v-model="card.description" />
</div>
<div>
<img v-if="card.picture" class="img-circle" style="width:150px" v-bind:src="card.picture" />
</div>
<div id="preview">
<h4>Preview</h4>
<ImageUpload :imgUrl="url" @newPicture="updatePicture"></ImageUpload>
<button @click="addCard" type="button" class="btn btn-primary btn-block" style="color:white">Save</button>
</div>
</form>
De addCard method calls a POST command to send information to the API:
addCard() {
const formData = new FormData();
formData.append('title',this.card.title);
formData.append('description',this.card.description);
formData.append('imagefile',this.selectedFile);
formData.append('game_id',this.currentGame.id);
// ...
}
SelectedFile and card.picture should be populated by the emitted method updatePicture:
updatePicture(imageData) {
console.log("emit ontvangen");
this.selectedFile = imageData.file;
this.card.picture = imageData.picture;
// ...
}
The ImageUpload Component itself looks like:
<template>
<div class="Image-Upload-wrapper Image-upload">
<div>
<input type="file" v-on:change="onFileChange" ref="fileUpload" id="file_picture_input" />
<label for="file_picture_input" class="upload_picture_button">Kies afbeelding</label>
</div>
<div id="croppie"></div>
<div class="upload-wrapper">
<button type="button" class="btn btn-primary btn-sm" v-on:click="setImage">
<!-- type="button"" anders vervest de knop de hele pagina " -->
Set image
</button>
</div>
</div>
</template>
<script>
// uitleg over Croppie::https://www.youtube.com/watch?v=kvNozA8M1HM
import Croppie from 'croppie';
export default {
props: [
'imgUrl'
],
mounted() {
this.setUpCroppie()
},
data() {
return {
croppie: null,
croppieImage: '',
selectedFile: '',
picture: '',
url: '',
}
},
methods: {
setUpCroppie() {
let el = document.getElementById('croppie');
this.croppie = new Croppie(el, {
viewport: { width: 200, height:200, type:'circle' },
boundary: { width: 220, height:220 },
showZoomer: true,
enableOrientation: true
});
this.croppie.bind({
url: this.url
});
},
setImage() {
this.croppie.result({
type: 'canvas',
size: 'viewport'
})
.then(blob =>fetch(blob))
.then(res => res.arrayBuffer)
.then(buf => new File([buf], this.croppieImage.name, { type:'image/png' }))
.then(file => {
this.croppieImage = file;
this.picture = URL.createObjectURL(this.croppieImage);
const imageData = {
picture: this.picture,
file: this.croppieImage
};
console.log("klaar voor emit...?");
this.$emit('newPicture', imageData);
this.url = '';
})
},
onFileChange(e) {
if (e.target.files && e.target.files.length > 0) {
this.selectedFile = e.target.files[0];
console.log("gekozen file is: " + this.selectedFile.name);
this.url = URL.createObjectURL(this.selectedFile);
this.croppie.bind({
url: this.url
});
} else {
// Geen afbeelding geselecteerd, instellen op een standaardwaarde (bijv. een lege string)
this.selectedFile = null;
this.url = '';
}
}
},
}
</script>
In the Console I do see: klaar voor emit...?, from the setImage method in the chold component.
I do not see the emit ontvangen from the emit method in the parent.
Consequently the required fields for the POST command are also not populated.
Can anyone see what (probably silly) mistake I am making?
Child Element
A child element needs to declare the emits.
Options API
Composition API
After this, it will forward the declared emits to the parent element in Vue.
Options API
Composition API
Parent Element