Reputation: 855
I have this structure in a view file, in Vue js (EDITED):
<template>
<div>
<h1>{{quiz.name}}</h1>
</div>
<div class="quizForm">
<div class="quizQuestContainer">
<div class="quizQuestions" v-for="(question, index) in quiz.questions"
:key = index >
<div v-if="index === questionIndex">
<h2>{{ question.text }}</h2>
<ol>
<li v-for="(response, index) in question.responses"
:key = index
>
<label>
<input type="radio"
:value="[response.res_value === question.solution]"
:name="index"
> {{response.res_text}}
</label>
</li>
</ol>
<button v-if="questionIndex > 0" v-on:click="prev">
prev
</button>
<button v-on:click="next">
next
</button>
</div>
</div>
<div v-if="questionIndex === quiz.questions.length">
<h2> Quiz finished</h2>
</div>
</div>
<div class="quizImage">
<div class="ImageArea">
<img :src="imageCrop(quiz.iiif)">
<br/>
</div>
</template>
<script>
import sourceData from '@/data.json'
export default {
props:{
id: {type: String, required: true}
},
data(){
return {
questionIndex: 0,
}
},
computed:{
quiz(){
return sourceData.quizzes.find(quiz =>quiz.id === parseInt(this.id))
},
},
methods: {
// Go to next question
next: function() {
this.questionIndex++;
},
// Go to previous question
prev: function() {
this.questionIndex--;
},
imageCrop: function(iiif) {
let image = new Image();
image.src = iiif;
let width = image.width;
let height = image.height;
return image.src + " this is width " + width + " and this is height " + height
}
</script>
Sometimes I get the right height and width, but sometimes I get 0 and 0. Are there some caching problems? Is methods the right way to do this? I am new to Vue, so I don't understand very well how it works.
EDIT: If I use
image.onload = () => {
let height = image.height;
let width = image.width;
return image.src + " this is width " + width + " and this is height " + height
}
Nothing appear on the page. I tried to put an error handling like that:
nameError: function(){
try {
const error = new Error();
return error.message;
} catch (ex) {
return ex.message;
}
and it generates an event with this in the DOM:
const invoker = (e) => { // async edge case #6566: inner click event triggers patch, event handler // attached to outer element during patch, and triggered again. This // happens because browsers fire microtask ticks between event propagation. // the solution is simple: we save the timestamp when a handler is attached, // and the handler would only fire if the event passed to it was fired // AFTER it was attached. const timeStamp = e.timeStamp || _getNow(); if (skipTimestampCheck || timeStamp >= invoker.attached - 1) { callWithAsyncErrorHandling(patchStopImmediatePropagation(e, invoker.value), instance, 5 /* NATIVE_EVENT_HANDLER */, [e]); }
The data JSON is like that:
{
"quizzes": [
{
"name": "Quiz A",
"slug": "quiz-1",
"image": "D000533S_AlbiniW_ST40.jpg",
"iiif": "https://gallica.bnf.fr/iiif/ark:/12148/btv1b9042253v/f1/full/,550/0/native.jpg",
"id": 1,
"description":
"First quiz",
"questions": [
{
"text": "Question 1",
"responses": [
{"res_text": "Wrong",
"res_value": "a"
},
{"res_text": "Right",
"res_value": "b"
}
],
"solution": "a"
},
{
"text": "Question 2",
"responses": [
{"res_text": "Right",
"res_value": "a"
},
{"res_text": "Wrong",
"res_value": "b"
}
],
"solution": "b"
}
]
},
etc.
Upvotes: 1
Views: 944
Reputation: 698
As pointed in the comments by @theshark, creating an image to retrieve its dimensions is an asynchronous process. AFAIK, you won't be able to use the result of such method in the rendering.
It's unclear what is it you're trying to achieve, but the easiest way would be to store the dimensions in your component's data once your imageCrop
method got them. This method won't have to be called in the template, but rather in a created
hook.
You can directly bind you component's data in your template:
<template>
<div>
<div>
<h1>{{quiz.name}}</h1>
</div>
<div class="myImageData">
{{ src }} this is width {{ width }} and this is height {{ height }}
</div>
<div class="quizImage">
<div class="ImageArea">
<img :src="src">
<br/>
</div>
</div>
<button @click="prev">⬅️</button>
<button @click="next">➡️</button>
</div>
</template>
<script>
import sourceData from '@/data.json'
export default {
props:{
id: {type: String, required: true}
},
data() {
return {
src: "",
width: 0,
height: 0,
questionIndex: 0,
}
},
computed:{
quiz(){
return sourceData.quizzes.find(quiz => quiz.id === this.questionIndex);
},
},
updated() {
this.imageCrop();
},
created() {
this.questionIndex = parseInt(this.id);
this.imageCrop();
},
methods: {
next: function() {
this.questionIndex++;
},
prev: function() {
this.questionIndex--;
},
imageCrop: function() {
const iiif = this.quiz.iiif;
let image = new Image();
image.onload = (event) => {
this.width = event.target.width;
this.height = event.target.height;
this.src = event.target.src;
}
image.src = iiif;
}
}
}
</script>
(after seeing the complete code I'd advise splitting the logic into smaller components)
Upvotes: 2