Vue 3 应用程序错误:为什么在任何点击事件发生之前执行此方法?
Vue 3 app bug: why is this method executed before any click event occurs?
我正在使用 Vue 3 和 Bootstrap 4 构建一个测验应用程序。
我有这种方法来检查点击的答案是否是(与正确答案相同):
checkAnswer(answer) {
return answer == this.results[this.questionCount]["correct_answer"];
}
应该在点击列表项时执行,如下所示:
<li v-for="answer in answers" @click="checkAnswer(answer)" :class="{'text-white bg-success' : checkAnswer(answer)}">{{answer}}</li>
如果点击的答案是正确,列表项应该添加类 text-white bg-success
,否则 应该加上text-white bg-danger
.
const quizApp = {
data() {
return {
questionCount: 0,
results: [
{
question: "The book "The Little Prince" was written by...",
correct_answer: "Antoine de Saint-Exupéry",
incorrect_answers: [
"Miguel de Cervantes Saavedra",
"Jane Austen",
"F. Scott Fitzgerald"
]
},
{
question:
"Which novel by John Grisham was conceived on a road trip to Florida while thinking about stolen books with his wife?",
correct_answer: "Camino Island",
incorrect_answers: ["Rogue Lawyer", "Gray Mountain", "The Litigators"]
},
{
question:
"In Terry Pratchett's Discworld novel 'Wyrd Sisters', which of these are not one of the three main witches?",
correct_answer: "Winny Hathersham",
incorrect_answers: [
"Granny Weatherwax",
"Nanny Ogg",
"Magrat Garlick"
]
}
]
};
},
methods: {
nextQuestion() {
if (this.questionCount < this.results.length - 1) {
this.questionCount++;
}
},
prevQuestion() {
if (this.questionCount >= 1) {
this.questionCount--;
}
},
checkAnswer(answer) {
// check if the clicked anwser is equal to the correct answer
return answer == this.results[this.questionCount]["correct_answer"];
},
shuffle(arr) {
var len = arr.length;
var d = len;
var array = [];
var k, i;
for (i = 0; i < d; i++) {
k = Math.floor(Math.random() * len);
array.push(arr[k]);
arr.splice(k, 1);
len = arr.length;
}
for (i = 0; i < d; i++) {
arr[i] = array[i];
}
return arr;
}
},
computed: {
answers() {
let incorrectAnswers = this.results[this.questionCount][
"incorrect_answers"
];
let correctAnswer = this.results[this.questionCount]["correct_answer"];
// return all answers, shuffled
return this.shuffle(incorrectAnswers.concat(correctAnswer));
}
}
};
Vue.createApp(quizApp).mount("#quiz_app");
#quiz_app {
height: 100vh;
}
.container {
flex: 1;
}
.quetions .card-header {
padding-top: 1.25rem;
padding-bottom: 1.25rem;
}
.quetions .card-footer {
padding-top: 0.7rem;
padding-bottom: 0.7rem;
}
.answers li {
cursor: pointer;
display: block;
padding: 7px 15px;
margin-bottom: 5px;
border-radius: 6px;
border: 1px solid rgba(0, 0, 0, 0.1);
background: #fff;
}
.answers li:last-child {
margin-bottom: 0;
}
.answers li:hover {
background: #fafafa;
}
.pager {
list-style-type: none;
margin: 0;
padding: 0;
display: flex;
justify-content: space-between;
}
.pager li > a {
display: inline-block;
padding: 5px 10px;
text-align: center;
width: 100px;
background-color: #fff;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 999px;
text-decoration: none !important;
color: #fff;
}
.pager li > a.disabled {
pointer-events: none;
background-color: #9d9d9d !important;
}
.logo {
width: 30px;
}
.nav-item {
width: 100%;
}
.card {
width: 100%;
}
@media (min-width: 768px) {
.nav-item {
width: auto;
}
.card {
width: 67%;
}
}
@media (min-width: 992px) {
.card {
width: 50%;
}
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/vue@next"></script>
<div id="quiz_app" class="container quetions d-flex align-items-center justify-content-center my-3">
<div v-if="results.length" class="card shadow-sm">
<div class="card-header bg-light h6">
{{results[questionCount]['question']}}
</div>
<div class="card-body">
<ul class="answers list-unstyled m-0">
<li v-for="answer in answers" @click="checkAnswer(answer)" :class="{'text-white bg-success' : checkAnswer(answer)}">{{answer}}</li>
</ul>
</div>
<div class="card-footer bg-white">
<ul class="pager">
<li><a href="#" @click="prevQuestion" class="bg-dark" :class="{'disabled' : questionCount == 0}">Previous</a></li>
<li class="d-flex align-items-center text-secondary font-weight-bold small">Question {{questionCount + 1}} of {{results.length}}</li>
<li><a href="#" class="bg-dark" :class="{'disabled' : questionCount == results.length - 1}" @click="nextQuestion">Next</a></li>
</ul>
</div>
</div>
</div>
问题
令我惊讶的是,checkAnswer(answer)
方法在任何点击之前(并且没有)执行。
问题
我做错了什么?
这是在点击前执行:
:class="{'text-white bg-success' : checkAnswer(answer)}"
.
您需要将每个答案的状态保存在一个变量中,并在方法中更新它。
并且作为侧节点,循环元素推荐使用:key
问题是 checkAnswer
在 html 模板中被引用,因此它将在渲染时立即执行。您可能想尝试将 class 设置为某个计算的 属性 而不是函数。
<li v-for="answer in answers" @click="checkAnswer(answer)" :class="{'text-white bg-success' : checkAnswer(answer)}">{{answer}}</li>
已更新
如果在处理程序外部使用,checkAnswer()
会立即被调用。
也许这会有所帮助,当调用 checkAnswer()
时,存储所选答案 selectedAnswer
并检查答案是否正确 isCorrect
,并使用这 2 个状态来比较循环答案.
<li
v-for="answer in answers"
:key="answer"
@click="checkAnswer(answer)"
:class="{
'text-white bg-success' : (selectedAnswer === answer && isCorrect),
'text-white bg-danger' : (selectedAnswer === answer && !isCorrect)
}"
>
{{answer}}
</li>
data() {
return {
isCorrect: false,
selectedAnswer: ''
...
}
},
methods: {
checkAnswer(answer) {
// check if the clicked anwser is equal to the correct answer
this.selectedAnswer = answer
if (answer == this.results[this.questionCount]["correct_answer"]) {
this.isCorrect = true
} else {
this.isCorrect = false
}
},
}
我正在使用 Vue 3 和 Bootstrap 4 构建一个测验应用程序。
我有这种方法来检查点击的答案是否是(与正确答案相同):
checkAnswer(answer) {
return answer == this.results[this.questionCount]["correct_answer"];
}
应该在点击列表项时执行,如下所示:
<li v-for="answer in answers" @click="checkAnswer(answer)" :class="{'text-white bg-success' : checkAnswer(answer)}">{{answer}}</li>
如果点击的答案是正确,列表项应该添加类 text-white bg-success
,否则 应该加上text-white bg-danger
.
const quizApp = {
data() {
return {
questionCount: 0,
results: [
{
question: "The book "The Little Prince" was written by...",
correct_answer: "Antoine de Saint-Exupéry",
incorrect_answers: [
"Miguel de Cervantes Saavedra",
"Jane Austen",
"F. Scott Fitzgerald"
]
},
{
question:
"Which novel by John Grisham was conceived on a road trip to Florida while thinking about stolen books with his wife?",
correct_answer: "Camino Island",
incorrect_answers: ["Rogue Lawyer", "Gray Mountain", "The Litigators"]
},
{
question:
"In Terry Pratchett's Discworld novel 'Wyrd Sisters', which of these are not one of the three main witches?",
correct_answer: "Winny Hathersham",
incorrect_answers: [
"Granny Weatherwax",
"Nanny Ogg",
"Magrat Garlick"
]
}
]
};
},
methods: {
nextQuestion() {
if (this.questionCount < this.results.length - 1) {
this.questionCount++;
}
},
prevQuestion() {
if (this.questionCount >= 1) {
this.questionCount--;
}
},
checkAnswer(answer) {
// check if the clicked anwser is equal to the correct answer
return answer == this.results[this.questionCount]["correct_answer"];
},
shuffle(arr) {
var len = arr.length;
var d = len;
var array = [];
var k, i;
for (i = 0; i < d; i++) {
k = Math.floor(Math.random() * len);
array.push(arr[k]);
arr.splice(k, 1);
len = arr.length;
}
for (i = 0; i < d; i++) {
arr[i] = array[i];
}
return arr;
}
},
computed: {
answers() {
let incorrectAnswers = this.results[this.questionCount][
"incorrect_answers"
];
let correctAnswer = this.results[this.questionCount]["correct_answer"];
// return all answers, shuffled
return this.shuffle(incorrectAnswers.concat(correctAnswer));
}
}
};
Vue.createApp(quizApp).mount("#quiz_app");
#quiz_app {
height: 100vh;
}
.container {
flex: 1;
}
.quetions .card-header {
padding-top: 1.25rem;
padding-bottom: 1.25rem;
}
.quetions .card-footer {
padding-top: 0.7rem;
padding-bottom: 0.7rem;
}
.answers li {
cursor: pointer;
display: block;
padding: 7px 15px;
margin-bottom: 5px;
border-radius: 6px;
border: 1px solid rgba(0, 0, 0, 0.1);
background: #fff;
}
.answers li:last-child {
margin-bottom: 0;
}
.answers li:hover {
background: #fafafa;
}
.pager {
list-style-type: none;
margin: 0;
padding: 0;
display: flex;
justify-content: space-between;
}
.pager li > a {
display: inline-block;
padding: 5px 10px;
text-align: center;
width: 100px;
background-color: #fff;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 999px;
text-decoration: none !important;
color: #fff;
}
.pager li > a.disabled {
pointer-events: none;
background-color: #9d9d9d !important;
}
.logo {
width: 30px;
}
.nav-item {
width: 100%;
}
.card {
width: 100%;
}
@media (min-width: 768px) {
.nav-item {
width: auto;
}
.card {
width: 67%;
}
}
@media (min-width: 992px) {
.card {
width: 50%;
}
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/vue@next"></script>
<div id="quiz_app" class="container quetions d-flex align-items-center justify-content-center my-3">
<div v-if="results.length" class="card shadow-sm">
<div class="card-header bg-light h6">
{{results[questionCount]['question']}}
</div>
<div class="card-body">
<ul class="answers list-unstyled m-0">
<li v-for="answer in answers" @click="checkAnswer(answer)" :class="{'text-white bg-success' : checkAnswer(answer)}">{{answer}}</li>
</ul>
</div>
<div class="card-footer bg-white">
<ul class="pager">
<li><a href="#" @click="prevQuestion" class="bg-dark" :class="{'disabled' : questionCount == 0}">Previous</a></li>
<li class="d-flex align-items-center text-secondary font-weight-bold small">Question {{questionCount + 1}} of {{results.length}}</li>
<li><a href="#" class="bg-dark" :class="{'disabled' : questionCount == results.length - 1}" @click="nextQuestion">Next</a></li>
</ul>
</div>
</div>
</div>
问题
令我惊讶的是,checkAnswer(answer)
方法在任何点击之前(并且没有)执行。
问题
我做错了什么?
这是在点击前执行:
:class="{'text-white bg-success' : checkAnswer(answer)}"
.
您需要将每个答案的状态保存在一个变量中,并在方法中更新它。
并且作为侧节点,循环元素推荐使用:key
问题是 checkAnswer
在 html 模板中被引用,因此它将在渲染时立即执行。您可能想尝试将 class 设置为某个计算的 属性 而不是函数。
<li v-for="answer in answers" @click="checkAnswer(answer)" :class="{'text-white bg-success' : checkAnswer(answer)}">{{answer}}</li>
已更新
如果在处理程序外部使用,checkAnswer()
会立即被调用。
也许这会有所帮助,当调用 checkAnswer()
时,存储所选答案 selectedAnswer
并检查答案是否正确 isCorrect
,并使用这 2 个状态来比较循环答案.
<li
v-for="answer in answers"
:key="answer"
@click="checkAnswer(answer)"
:class="{
'text-white bg-success' : (selectedAnswer === answer && isCorrect),
'text-white bg-danger' : (selectedAnswer === answer && !isCorrect)
}"
>
{{answer}}
</li>
data() {
return {
isCorrect: false,
selectedAnswer: ''
...
}
},
methods: {
checkAnswer(answer) {
// check if the clicked anwser is equal to the correct answer
this.selectedAnswer = answer
if (answer == this.results[this.questionCount]["correct_answer"]) {
this.isCorrect = true
} else {
this.isCorrect = false
}
},
}