Reputation: 155
I have parentform with a child form that allows to add games. The childform has option to edit games, and the grandchildform has option to add cards to the game, using a Imageupload component.
The idea is that admin can add cards using the cardform, whic is opened by clicking: add card, and which must close when card is added. Then the list of existing must be refreshed ( does not woreither, ...)
When I add a card to a game, and this is succesfull I set ShowCardForm=false, but this is not changed in the devtools VUE window.
While console shows that "ShowCardForm must be false now: false", as expected from the code.
I have been messing around for hours now, can anyone see what I am missing? (ChatGPT was a great help either...)
Here is part of the script:
alert('Card added/updated successfully');
this.showCardForm = false;
console.log('ShowCardForm must be false now:', this.showCardForm);
this.resetForm();
Can anyone see how I can solve this?
Here is the whole script: GameAdmin:
<template>
<Navibar ></Navibar>
<div class ="custom-container" >
<br>
<br>
<div v-if="!showGameForm" class="text-center">
<h2>Game-beheer </h2>
<button @click="toggleShowGameForm" class="btn btn-info m-1">Nieuw spel maken</button>
</div>
<div class="card text-center card-bg" v-if="showGameForm">
<div class="card-body">
<form class="mb-3" >
<div class="row">
<div class="col">
<label><h4> Gamenaam: </h4></label>
</div>
<div class="col">
<input type="text" class="form-control" placeholder="naam" v-model="game.naam">
</div>
</div>
<div class="mt-2">
<div class="row">
<div class="col">
<label><h4> Stel de status in: </h4></label>
</div>
<div class="col">
<div class="d-flex justify-content-around">
<input class="btn-check" type="radio" name="game_status" id="closed" v-model="game.game_status" value="closed">
<label class="btn btn-outline-primary" for="closed"> Niet actief </label>
<input class="btn-check" type="radio" name="game_status" id="open" v-model="game.game_status" value='open' checked="checked">
<label class="btn btn-outline-primary" for="open"> Kaarten maken </label>
<input class="btn-check" type="radio" name="game_status" id="actief" v-model="game.game_status" value="actief">
<label class="btn btn-outline-primary" for="actief"> Actief </label>
<br>
</div>
</div>
</div>
</div>
<br><br>
<div class="d-flex justify-content-between align-items-center">
<button @click="toggleShowGameForm" class="btn btn-info">Terug</button>
<button @click= "addgame" type="button" class="btn btn-primary">Save</button>
</div>
<ManageCards :key = "currentGame.id" v-if="currentGame && Object.keys(currentGame).length"></ManageCards>
</form>
</div>
</div>
<div v-if="!gamesExist">
Er zijn nog geen games gemaakt.
</div>
<div v-if="!showGameForm && gamesExist"> <!-- Laat lijst met spellen zien als er geen editscherm van een bepaald spel open staat -->
<div class="card text-center card-bg mt-2" v-for="game in games.slice().reverse()" v-bind:key="game.id" >
<div class="card-body m-2">
<h5 class="card-title"> Gamenaam: {{game.naam}} </h5>
<ul class="list-group list-group-flush">
<!-- <li class="list-group-item">Modus: {{game.game_mode}}</li> -->
<li class="list-group-item">Status: {{game.game_status}}</li>
<li class="list-group-item">Pin: {{game.pin}}</li>
</ul>
<button @click="deletegame(game.id)" class="btn btn-danger m-1" style="width:100px;color:white"> Delete </button>
<button @click="toggleShowGameForm(); editgame(game)" class="btn btn-warning m-1" style="width:100px;color:black"> Edit </button>
<button @click="setCurrentGame(game.pin)" class="btn btn-warning m-1" style="width:100px;color:black"> Run {{game.pin}}</button>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
mounted() {
console.log('component mounted');
this.$store.commit('SET_CURRENT_GAME', {}); // Ensure currentGame is initialized as an empty object
this.getGames();
},
computed: {
...mapState([
'games' , 'token','currentGame'
]),
gamesExist() {
return this.games && this.games.length > 0;
},
},
data() {
return{
// games:[],
game:{
id:'',
naam:'',
game_mode:'',
game_status:'',
pin: 0,
},
game_id:'',
edit:false,
selected:'',
showGameForm: false,
currentGameError:false,
checked:false,
}
},
created(){
//this.fetchgames();
},
methods: {
...mapActions([ 'getGames', 'addGame', 'retrieveGameByPin' ]),
toggleShowGameForm(){
this.$store.commit('SET_CURRENT_GAME',{});
this.game.naam='naam';
this.game.game_mode='open';
this.game.game_status='open';
this.game.pin='0';
this.showGameForm = !this.showGameForm;
},
async setCurrentGame(pin){
console.log("ik krijg deze pin door:"+ pin);
await this.retrieveGameByPin(pin)
.then(res => {
console.log('gelukt met pin' + pin) // als status 200, dan resolvet de promise uit de action, and komen we hier uit
console.log("retrievegamebypin result == " + JSON.stringify(res));
})
.catch(err => {
//this.currentGameError = true;
console.error(err);
});
},
// DELETE GAME WERKT NOG NIET, KAN PAS ALS ALLE CARDS OOK VERWIJDERD ZIJN..
deletegame(id){
if(confirm('Are you sure?')){
const formData = new FormData();
formData.append('_method', 'DELETE'); // axios setting
const config = {
headers: { Authorization: `Bearer `+ this.token}
};
axios.post(`../api/games/${id}`,
formData,
config
)
//.then(res => res.json())
.then(data => {
// De game is verwijderd, nu vernieuw de lijst met games
this.getGames()
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
})
.catch(err => console.log(err));
}
},
addgame(){
// de informatie voor de game staat inthis.$game, nu omzetten naar FormData om file toe te voegen aan de request zodat api die kan opslaan, api gaat ook naam bedenken voor de game.
// via fetchgames() komt die naam dadelijk terug in game.picture
const formData = new FormData();
formData.append('naam',this.game.naam);
formData.append('game_mode',this.game.game_mode);
formData.append('game_status',this.game.game_status);
//formData.append('pin',this.game.pin); //this.game.pin);
//formData.append('user_id', 11); //this.game.user_id); dit wordt aan de hand van de api-token op de server ingevuld
if(this.edit === false){
//add
console.log("adding" + formData);
this.addGame(formData)
.then(data => {
this.game.naam='';
this.game.game_mode='';
this.game.game_status='';
this.game.pin='';
// this.game.user_id=''; dit wordt aan de hand van de api-token op de server ingevuld
this.showGameForm=false;
//alert('game added');
this.getGames()
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
})
.catch(err => console.log(err));
} else{
//update
console.log("updating");
formData.append('_method', 'PUT'); // axios setting
const config = {
headers: { Authorization: `Bearer ${this.token}` }
};
axios.post(`../api/games/${this.game.id}`,
formData,
config
)
.then(res => console.log(res))
.then(data => {
this.game.naam='';
this.game.game_mode='';
this.game.game_status='';
alert('game updated_admin.vue');
this.getGames();
})
.catch(err => console.log(err));
}
},
editgame(game){
this.currentGame = this.retrieveGameByPin(game.pin);
this.edit=true;
this.game.id=game.id;
this.game.game_id=game.id;
this.game.naam=game.naam;
this.game.game_mode=game.game_mode;
this.game.game_status=game.game_status;
this.game.pin=game.pin;
},
}
}
</script>
ManageCards:
<template>
<div class="custom-container" v-if="currentGame.id != undefined">
<!-- Container wordt alleen weergegeven als er een currentGame is -->
<div class="text-center">
<br><br>
<!-- Weergeven van de naam van het huidige spel -->
<h1>{{ currentGame.naam }}</h1>
<!-- Knop om het formulier voor het maken van een nieuwe kaart te tonen/verbergen -->
<button type="button" @click="toggleShowCardForm" class="btn btn-info m-1">Nieuwe kaart maken</button>
<!-- Formulier voor het maken/bewerken van een kaart -->
<div class="card text-center card-bg" v-if="showCardForm">
<h5 class="card-header">Kaart</h5>
<div class="card-body">
<form class="mb-3" >
<div class="form-group">
<!-- Beschrijving van de kaart -->
<h4>{{ card.description }}</h4>
<!-- Tekstveld voor het bewerken van de beschrijving -->
<textarea class="form-control" :placeholder="card.description" v-model="card.description"></textarea>
</div>
<div>
<!-- Voorbeeldweergave van de afbeelding van de kaart -->
<img v-if="card.picture" class="img-circle" style="width:150px" :src="card.picture"/>
</div>
<div id="preview">
<h4> Preview</h4>
<!-- Component voor het uploaden van afbeeldingen -->
<ImageUpload ref="imageUpload" :imgUrl="url" @newPicture="updatePicture"></ImageUpload>
</div>
<br>
<!-- Knop om de kaart op te slaan -->
<button @click="addCard();" type="button" class="btn btn-primary btn-block" style="color:white">Save</button>
</form>
</div>
</div>
</div>
<!-- Lijst van kaarten -->
<div v-for="card in cards" :key="card.id">
<div class="card text-center card-bg mt-2">
<div class="card-body mb-2" style="width: 100%">
<div class="row">
<div class="col">
<!-- Beschrijving van de kaart -->
<h3>{{ card.description }}</h3>
</div>
<div class="col">
<!-- Afbeelding van de kaart -->
<img class="img-circle" style="width:150px" :src="card.picture" alt="Card Image">
</div>
<div class="col">
<br>
<!-- Knoppen voor het verwijderen en bewerken van de kaart -->
<span @click="deleteCard(card.id)"><i class="bi bi-trash m-1"></i></span>
<span @click="editCard(card)"><i class="bi bi-pencil m-1"></i></span>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
import axios from 'axios';
import ImageUpload from './ImageUpload.vue';
export default {
components: {
ImageUpload, // Component voor het uploaden van afbeeldingen
},
watch: {
showCardForm(newVal, oldVal) {
console.log(`showCardForm changed from ${oldVal} to ${newVal}`);
}
},
data() {
return {
card: {
id: '', // ID van de kaart
title: 'title', // Titel van de kaart
description: 'naam', // Beschrijving van de kaart
picture: '', // Afbeelding van de kaart
},
edit: false, // Boolean om te checken of we een kaart aan het bewerken zijn
selectedFile: '', // Bestand geselecteerd voor uploaden
showCardForm: false, // Boolean om het formulier te tonen/verbergen
url: '', // URL van de geüploade afbeelding
successMessage: '', // Bericht voor succesvolle bewerkingen
};
},
computed: {
...mapState(['cards', 'currentGame', 'token']), // Vuex state voor kaarten, huidig spel en token
},
async mounted() {
// Wanneer de component wordt geladen
try {
await this.getCards(this.currentGame.id); // Haal de kaarten op voor het huidige spel
console.log('component mounted');
} catch (error) {
console.error('Error during mounted hook:', error);
}
},
methods: {
...mapActions(['getCards']), // Vuex acties voor het ophalen van kaarten
toggleShowCardForm() {
this.showCardForm = !this.showCardForm; // Toon/verberg het formulier
this.url = ''; // Reset de url wanneer het formulier wordt weergegeven of verborgen
this.getCards(this.currentGame.id); // Haal opnieuw de kaarten op
},
async deleteCard(id) {
// Verwijder een kaart
if (confirm('Are you sure?')) {
const formData = new FormData();
formData.append('_method', 'DELETE'); // Methode voor het verwijderen instellen
const config = {
headers: { Authorization: `Bearer ${this.token}` }, // Configuratie voor de aanvraag met token
};
try {
await axios.post(`../api/cards/${id}`, formData, config); // Verwijder kaart
alert('Card removed');
await this.getCards(this.currentGame.id); // Haal opnieuw de kaarten op
} catch (error) {
console.error('Error deleting card:', error);
}
}
},
async addCard() {
// Voeg een nieuwe kaart toe of werk een bestaande kaart bij
try {
await this.$refs.imageUpload.setImage(); // Zet de imageUrl naar de picture en sla de picture of als file
if (!this.selectedFile) {
alert('Please select an image file');
return;
}
const formData = new FormData();
formData.append('title', this.card.title); // Voeg de titel toe aan formData
formData.append('description', this.card.description); // Voeg de beschrijving toe aan formData
formData.append('imagefile', this.selectedFile); // Voeg de afbeelding toe aan formData
formData.append('game_id', this.currentGame.id); // Voeg het spel-ID toe aan formData
let response;
if (!this.edit) {
// Nieuwe kaart toevoegen
response = await fetch(`api/cards`, {
method: 'POST',
body: formData,
}).then(response => response.json());
} else {
// Bestaande kaart bijwerken
formData.append('_method', 'PUT');
const config = {
headers: { Authorization: `Bearer ${this.token}` },
};
response = await axios.post(`../api/cards/${this.card.id}`, formData, config).then(res => res.data);
}
if (response && response.id) {
alert('Card added/updated successfully');
this.showCardForm = false;
console.log('ShwoCardForm must be false now:', this.showCardForm);
this.resetForm();
} else {
alert('Failed to add/update card');
}
} catch (error) {
console.error('Error updating card:', error);
alert('An error occurred while adding/updating the card');
}
},
editCard(card) {
// Zet de huidige kaart voor bewerking
window.scrollTo(0, 0); // Scroll naar boven
this.edit = true; // Zet edit mode aan
this.showCardForm = true; // Toon het formulier
this.card = { ...card }; // Kopieer de kaartgegevens
},
updatePicture(imageData) {
// Werk de afbeelding bij
this.selectedFile = imageData.file; // Zet het geselecteerde bestand
this.card.picture = imageData.picture; // Zet de afbeelding URL
},
resetForm() {
// Reset het formulier
this.card = {
id: '',
title: '',
description: '',
picture: '',
};
this.url = ''; // Reset de URL
this.edit = false; // Zet edit mode uit
},
},
};
</script>
and IMageUpload:
<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">
<!-- type="button"" anders vervest de knop de hele pagina " -->
<!-- <button type="button" class="btn btn-primary btn-sm" v-on:click="setImage"> SetImage! </button>-->
</div>
</div>
</template>
<script>
// uitleg over Croppie::https://www.youtube.com/watch?v=kvNozA8M1HM
import Croppie from 'croppie';
export default {
props:[
'imgUrl'
],
emits:['newPicture'
],
mounted(){
this.setUpCroppie()
},
data(){
return{
croppie:null,
croppieImage:'',
selectedFile:'',
picture:'',
url:this.imgUrl || '',
}
},
//Zorg ervoor dat imgUrl updates correct worden doorgegeven aan Croppie.
watch: {
imgUrl(newVal) {
if (newVal) {
this.url = newVal;
this.croppie.bind({ url: newVal });
}
}
},
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
})
.catch(error => console.error('Error binding croppie with url:', error));
},
setImage(){
return this.croppie.result({
type:'canvas',
size:'viewport'
})
.then(blob =>fetch(blob))
.then(res => {return res.arrayBuffer();})
.then(buf => {return 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='';
})
.catch(error => {
console.error("Error setting image:", error);
});
},
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
})
.catch(error => console.error('Error binding croppie with file url:', error));
} else {
// Geen afbeelding geselecteerd, instellen op een standaardwaarde (bijv. een lege string)
this.selectedFile = null;
this.url = '';
}
},
reset() {
this.selectedFile = null;
this.picture = '';
this.url = '';
this.croppie.bind({
url: ''
}).catch(error => console.error('Error binding croppie with reset url:', error));
}
},
}
</script>
Upvotes: 0
Views: 38