vue js class 绑定重复元素
vue js class binding on repetitious element
我想做一个简单的游戏来帮助孩子们阅读,实际上它是一种语言学习方法,名为'silent mode',反正这是jsfiddle link
https://jsfiddle.net/raminSafari/b7zhc98q/19/
例如,如果我想让学生阅读 'pen',我先指向 p(1),然后指向 e(2),最后指向 n(3),代码可以很好地处理具有唯一字母的单词,但是当这个词类似于 'dad' 时,它没有按照我想要的方式工作,我希望它显示 d(1)(3), a(2)
这是完整的简化代码(我知道它不可靠)
<template>
<div class = "container pt-5 mt-5">
<h1 class="text-center pb-5"><span style="color: red;"> {{ answer }} </span> just to clarify</h1> <!-- just to clarify -->
<div class="text-center">
<template id="keyboard" v-for="alphabet in alphabets" >
<template v-if = "alphabet == word.first ">
<span :class="{ 'active': firstActive, alphabet}"> {{ alphabet }} </span> <strong style="color: red; font-size: 10px;">{{ num1 }}</strong>
</template>
<template v-else-if = "alphabet == word.second ">
<span :class="{ 'active': secondActive, alphabet}"> {{ alphabet }} </span> <strong style="color: red; font-size: 10px;">{{ num2 }}</strong>
</template>
<template v-else-if = "alphabet == word.third ">
<span :class="{ 'active': thirdActive, alphabet}"> {{ alphabet }} </span> <strong style="color: red; font-size: 10px;">{{ num3 }}</strong>
</template>
<template v-else-if = "alphabet == word.forth ">
<span :class="{ 'active': forthActive, alphabet}"> {{ alphabet }} </span> <strong style="color: red; font-size: 10px;">{{ num4 }}</strong>
</template>
<template v-else-if = "alphabet == word.fifth ">
<span :class="{ 'active': forthActive, alphabet}"> {{ alphabet }} </span> <strong style="color: red; font-size: 10px;">{{ num5 }}</strong>
</template>
<template v-else>
<span class="alphabet"> {{ alphabet }} </span>
</template>
</template>
<div><button class = "btn btn-info mt-3" @click = "again">again</button></div>
</div>
</div>
</template>
<script>
export default {
data(){
return{
alphabets: ["p", "e", "m", "n", "d", "a", "s"],
firstActive: false,
secondActive: false,
thirdActive: false,
forthActive: false,
fifthActive: false,
index: 0,
words:[
{
first: 'p',
second: 'e',
third: 'n',
forth: '',
fifth: '',
answer : 'pen'
},
{
first: 'm',
second: 'a',
third: 'd',
forth: 'e',
fifth: '',
answer : 'made'
},
{
first: 'd',
second: 'a',
third: 'd',
forth: '',
fifth: '',
answer : 'dad'
},
],
word: [],
answer: '',
myVar1: null,
myVar2: null,
myVar3: null,
myVar4: null,
myVar5: null,
num1: '',
num2: '',
num3: '',
num4: '',
num5: ''
}
},
methods: {
shuffle(a) {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
},
getWord(){
this.word = this.words[this.index];
this.answer = this.word.answer;
},
again(){
clearTimeout(this.myVar1);
clearTimeout(this.myVar2);
clearTimeout(this.myVar3);
clearTimeout(this.myVar4);
clearTimeout(this.myVar5);
this.firstActive = false;
this.secondActive = false;
this.thirdActive = false;
this.forthActive = false;
this.fifthActive = false;
this.num1 = '';
this.num2 = '';
this.num3 = '';
this.num4 = '';
this.num5 = '';
if(this.index == this.words.length){
this.index = 0;
}else{
this.index++;
}
this.getWord();
this.showBorder();
},
showBorder(){
this.myVar1 = setTimeout(() => {
this.firstActive = true;
this.num1 = 1;
}, 2000);
this.myVar2 = setTimeout(() => {
this.secondActive = true;
this.num2 = 2;
}, 4000);
this.myVar3 = setTimeout(() => {
this.thirdActive = true;
this.num3 = 3;
}, 6000);
this.myVar4 = setTimeout(() => {
this.forthActive = true;
this.num4 = 4;
}, 8000);
this.myVar5 = setTimeout(() => {
this.fifthActive = true;
this.num5 = 5;
}, 10000);
}
},
created(){
this.words = this.shuffle(this.words);
this.getWord();
this.showBorder();
}
}
</script>
<style>
span.alphabet{
display: inline-block;
width: 70px;
height: 70px;
font-size: 30px;
font-weight: 600;
}
.active{
border: 2px solid red;
border-radius: 50%;
}
</style>
谢谢
答案不只是 copy-and-paste 代码,而且有点长。不想看的可以直接跳到结论。
我阅读了 jsfiddle,这里有一些解决您面临的问题的建议:
1。将数字视为字符串,而不是数字
之后你就会知道d(1)(3)问题不是显示2个数字,而是how to append the string " 1" 和 "3" 并显示它。
2。 Divide-and-conquer
将您的整个任务分为 3 个部分:
- 创建函数来控制应突出显示哪个字母并显示相应的序列。
- 创建一个组件,仅用于显示字母表和序列,例如d(1)(3).
- 定义触发渲染过程和重置应用程序状态的函数。
App.vue
这是 App.vue
中的数据
data: function() {
return {
word: "dad",
alphabets: ["p", "e", "m", "n", "d", "a", "s"],
hits: ["", "", "", "", "", "", ""],
handlers: [],
delay: 2000,
}
},
this.hits
将存储命中的相应序列。例如,对于 dad 的情况,点击将 最终 变为:["", "", "", "", "13", "2", ""]
.
这些是你应该在App.vue
中拥有的功能
- 循环每个字符。在
this.word
,并做相应的渲染
renderAnswer: function() {
var self = this;
for (var i = 0; i < this.word.length; i++) {
self.doSetTimeout(i, self);
}
}
- 控制何时进行渲染
doSetTimeout: function (i, self) {
self.handlers.push(setTimeout(function () {
self.updateHits(i, self)
}, self.delay * i));
}
- 更新
hits
数组,这将在每个字母上触发相应的渲染
updateHits: function(i, self) {
let char = self.word[i];
let index = self.alphabets.indexOf(char);
console.debug(char, index)
self.hits[index] += (i + 1);
self.hits = self.hits.filter(x => true); // force refresh list for binding
}
将你的大功能分成一些小功能,让每个小功能只做 1 件事。
注意:在这里,您将看到 renderAnswer()
调用 doSetTimeout()
和 doSetTimeout()
调用 updateHits()
。为了正确获取数据和函数,我们需要定义var self = this
,并在每个函数调用中传递self
。
Sub-component 字母表
在App.vue的模板中,需要定义一个<alphabet>
sub-component。该组件仅负责呈现特定字母表以及与该字母表相关的序列。
<alphabet
v-for="(char, index) in alphabets"
v-bind:key="char"
:alphabet="char"
v-bind:hits="hits[index]"
/>
您需要将字母表和相应的命中值传递给 sub-component 才能正确呈现组件。这个sub-component的代码应该是straight-forward所以这里就跳过了。在执行此部分时,您可能需要知道如何从 official documentaion 传递和使用 props
。
运行 应用
当你设置所有的函数和 sub-component 时,你应该能够在触发 renderAnswer()
.
时呈现 dad
的情况
重置状态
您可能需要为每个新词重置应用状态。因此,你还应该定义一个reset()
函数,将this.word
更新为一个新词,并将this.hits
中的每一项重置为空字符串。
如果您需要上述步骤的更多详细信息,请阅读 this gist。
结论
当您遇到一些编码问题时,您可以尝试re-phasing您遇到的问题。 提出正确的问题 将引导您使用更好的方法解决问题。
另一方面,把问题分解成更小的问题,一个一个解决。在这种情况下,尝试将大功能拆分为一些更小、更简单的功能。并且,尝试创建一个 sub-component 来完成渲染部分,同时将所有逻辑留给它的 parent.
希望你能解决你遇到的问题,帮助更多的孩子:)
你可以将你输入的字母存储在一个数组中,这样你就可以获得输入的字母的索引并显示它们。我建议像这样构造您的代码。这是重构代码https://jsfiddle.net/tk41d0wo/
的fiddle
<div id="app">
<div>
Current word is <span style="color: red;">{{ words[currentIndex] }}</span>
</div>
<div>
<button
v-for="letter in letters"
:key="letter"
@click="inputLetter(letter)"
:style="{ color: (inputs.includes(letter)) ? 'red': 'initial' }"
>
{{ letter }} <small>({{ getAllLetterPositions(letter).join(', ') }})</small>
</button>
</div>
<button @click="resetInputs">
Reset
</button>
</div>
并在您的组件中...
new Vue({
el: "#app",
data: {
currentIndex: 0,
words: ['made', 'sad', 'dad'],
letters: ['p', 'e', 'm', 'n', 'd', 'a', 's'],
inputs: [],
},
methods: {
inputLetter(letter) {
this.inputs.push(letter);
if(this.inputs.join('') == this.words[this.currentIndex]) {
this.currentIndex++;
this.resetInputs();
}
},
getAllLetterPositions(letter) {
return this.inputs.reduce((positions, current, index) => {
if(current == letter) {
return positions.concat(index + 1);
}
return positions;
}, []).sort(function(a, b){return a-b})
},
resetInputs() {
this.inputs = [];
}
}
})
我想做一个简单的游戏来帮助孩子们阅读,实际上它是一种语言学习方法,名为'silent mode',反正这是jsfiddle link
https://jsfiddle.net/raminSafari/b7zhc98q/19/
例如,如果我想让学生阅读 'pen',我先指向 p(1),然后指向 e(2),最后指向 n(3),代码可以很好地处理具有唯一字母的单词,但是当这个词类似于 'dad' 时,它没有按照我想要的方式工作,我希望它显示 d(1)(3), a(2)
这是完整的简化代码(我知道它不可靠)
<template>
<div class = "container pt-5 mt-5">
<h1 class="text-center pb-5"><span style="color: red;"> {{ answer }} </span> just to clarify</h1> <!-- just to clarify -->
<div class="text-center">
<template id="keyboard" v-for="alphabet in alphabets" >
<template v-if = "alphabet == word.first ">
<span :class="{ 'active': firstActive, alphabet}"> {{ alphabet }} </span> <strong style="color: red; font-size: 10px;">{{ num1 }}</strong>
</template>
<template v-else-if = "alphabet == word.second ">
<span :class="{ 'active': secondActive, alphabet}"> {{ alphabet }} </span> <strong style="color: red; font-size: 10px;">{{ num2 }}</strong>
</template>
<template v-else-if = "alphabet == word.third ">
<span :class="{ 'active': thirdActive, alphabet}"> {{ alphabet }} </span> <strong style="color: red; font-size: 10px;">{{ num3 }}</strong>
</template>
<template v-else-if = "alphabet == word.forth ">
<span :class="{ 'active': forthActive, alphabet}"> {{ alphabet }} </span> <strong style="color: red; font-size: 10px;">{{ num4 }}</strong>
</template>
<template v-else-if = "alphabet == word.fifth ">
<span :class="{ 'active': forthActive, alphabet}"> {{ alphabet }} </span> <strong style="color: red; font-size: 10px;">{{ num5 }}</strong>
</template>
<template v-else>
<span class="alphabet"> {{ alphabet }} </span>
</template>
</template>
<div><button class = "btn btn-info mt-3" @click = "again">again</button></div>
</div>
</div>
</template>
<script>
export default {
data(){
return{
alphabets: ["p", "e", "m", "n", "d", "a", "s"],
firstActive: false,
secondActive: false,
thirdActive: false,
forthActive: false,
fifthActive: false,
index: 0,
words:[
{
first: 'p',
second: 'e',
third: 'n',
forth: '',
fifth: '',
answer : 'pen'
},
{
first: 'm',
second: 'a',
third: 'd',
forth: 'e',
fifth: '',
answer : 'made'
},
{
first: 'd',
second: 'a',
third: 'd',
forth: '',
fifth: '',
answer : 'dad'
},
],
word: [],
answer: '',
myVar1: null,
myVar2: null,
myVar3: null,
myVar4: null,
myVar5: null,
num1: '',
num2: '',
num3: '',
num4: '',
num5: ''
}
},
methods: {
shuffle(a) {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
},
getWord(){
this.word = this.words[this.index];
this.answer = this.word.answer;
},
again(){
clearTimeout(this.myVar1);
clearTimeout(this.myVar2);
clearTimeout(this.myVar3);
clearTimeout(this.myVar4);
clearTimeout(this.myVar5);
this.firstActive = false;
this.secondActive = false;
this.thirdActive = false;
this.forthActive = false;
this.fifthActive = false;
this.num1 = '';
this.num2 = '';
this.num3 = '';
this.num4 = '';
this.num5 = '';
if(this.index == this.words.length){
this.index = 0;
}else{
this.index++;
}
this.getWord();
this.showBorder();
},
showBorder(){
this.myVar1 = setTimeout(() => {
this.firstActive = true;
this.num1 = 1;
}, 2000);
this.myVar2 = setTimeout(() => {
this.secondActive = true;
this.num2 = 2;
}, 4000);
this.myVar3 = setTimeout(() => {
this.thirdActive = true;
this.num3 = 3;
}, 6000);
this.myVar4 = setTimeout(() => {
this.forthActive = true;
this.num4 = 4;
}, 8000);
this.myVar5 = setTimeout(() => {
this.fifthActive = true;
this.num5 = 5;
}, 10000);
}
},
created(){
this.words = this.shuffle(this.words);
this.getWord();
this.showBorder();
}
}
</script>
<style>
span.alphabet{
display: inline-block;
width: 70px;
height: 70px;
font-size: 30px;
font-weight: 600;
}
.active{
border: 2px solid red;
border-radius: 50%;
}
</style>
谢谢
答案不只是 copy-and-paste 代码,而且有点长。不想看的可以直接跳到结论。
我阅读了 jsfiddle,这里有一些解决您面临的问题的建议:
1。将数字视为字符串,而不是数字
之后你就会知道d(1)(3)问题不是显示2个数字,而是how to append the string " 1" 和 "3" 并显示它。
2。 Divide-and-conquer
将您的整个任务分为 3 个部分:
- 创建函数来控制应突出显示哪个字母并显示相应的序列。
- 创建一个组件,仅用于显示字母表和序列,例如d(1)(3).
- 定义触发渲染过程和重置应用程序状态的函数。
App.vue
这是 App.vue
data: function() {
return {
word: "dad",
alphabets: ["p", "e", "m", "n", "d", "a", "s"],
hits: ["", "", "", "", "", "", ""],
handlers: [],
delay: 2000,
}
},
this.hits
将存储命中的相应序列。例如,对于 dad 的情况,点击将 最终 变为:["", "", "", "", "13", "2", ""]
.
这些是你应该在App.vue
- 循环每个字符。在
this.word
,并做相应的渲染
renderAnswer: function() {
var self = this;
for (var i = 0; i < this.word.length; i++) {
self.doSetTimeout(i, self);
}
}
- 控制何时进行渲染
doSetTimeout: function (i, self) {
self.handlers.push(setTimeout(function () {
self.updateHits(i, self)
}, self.delay * i));
}
- 更新
hits
数组,这将在每个字母上触发相应的渲染
updateHits: function(i, self) {
let char = self.word[i];
let index = self.alphabets.indexOf(char);
console.debug(char, index)
self.hits[index] += (i + 1);
self.hits = self.hits.filter(x => true); // force refresh list for binding
}
将你的大功能分成一些小功能,让每个小功能只做 1 件事。
注意:在这里,您将看到 renderAnswer()
调用 doSetTimeout()
和 doSetTimeout()
调用 updateHits()
。为了正确获取数据和函数,我们需要定义var self = this
,并在每个函数调用中传递self
。
Sub-component 字母表
在App.vue的模板中,需要定义一个<alphabet>
sub-component。该组件仅负责呈现特定字母表以及与该字母表相关的序列。
<alphabet
v-for="(char, index) in alphabets"
v-bind:key="char"
:alphabet="char"
v-bind:hits="hits[index]"
/>
您需要将字母表和相应的命中值传递给 sub-component 才能正确呈现组件。这个sub-component的代码应该是straight-forward所以这里就跳过了。在执行此部分时,您可能需要知道如何从 official documentaion 传递和使用 props
。
运行 应用
当你设置所有的函数和 sub-component 时,你应该能够在触发 renderAnswer()
.
dad
的情况
重置状态
您可能需要为每个新词重置应用状态。因此,你还应该定义一个reset()
函数,将this.word
更新为一个新词,并将this.hits
中的每一项重置为空字符串。
如果您需要上述步骤的更多详细信息,请阅读 this gist。
结论
当您遇到一些编码问题时,您可以尝试re-phasing您遇到的问题。 提出正确的问题 将引导您使用更好的方法解决问题。
另一方面,把问题分解成更小的问题,一个一个解决。在这种情况下,尝试将大功能拆分为一些更小、更简单的功能。并且,尝试创建一个 sub-component 来完成渲染部分,同时将所有逻辑留给它的 parent.
希望你能解决你遇到的问题,帮助更多的孩子:)
你可以将你输入的字母存储在一个数组中,这样你就可以获得输入的字母的索引并显示它们。我建议像这样构造您的代码。这是重构代码https://jsfiddle.net/tk41d0wo/
的fiddle<div id="app">
<div>
Current word is <span style="color: red;">{{ words[currentIndex] }}</span>
</div>
<div>
<button
v-for="letter in letters"
:key="letter"
@click="inputLetter(letter)"
:style="{ color: (inputs.includes(letter)) ? 'red': 'initial' }"
>
{{ letter }} <small>({{ getAllLetterPositions(letter).join(', ') }})</small>
</button>
</div>
<button @click="resetInputs">
Reset
</button>
</div>
并在您的组件中...
new Vue({
el: "#app",
data: {
currentIndex: 0,
words: ['made', 'sad', 'dad'],
letters: ['p', 'e', 'm', 'n', 'd', 'a', 's'],
inputs: [],
},
methods: {
inputLetter(letter) {
this.inputs.push(letter);
if(this.inputs.join('') == this.words[this.currentIndex]) {
this.currentIndex++;
this.resetInputs();
}
},
getAllLetterPositions(letter) {
return this.inputs.reduce((positions, current, index) => {
if(current == letter) {
return positions.concat(index + 1);
}
return positions;
}, []).sort(function(a, b){return a-b})
},
resetInputs() {
this.inputs = [];
}
}
})