【问题标题】:Implementing a "fill-in-the-blank" component with Vue.js使用 Vue.js 实现“填充空白”组件
【发布时间】:2020-01-07 14:29:16
【问题描述】:

基本上,我想做的是在 Vue.js 中实现一个填空系统,但我在想出一个合适的设计来处理某些功能时遇到了麻烦。

到目前为止,我已经做到了这一点:Vue 组件采用了一个名为 sentence 的道具,其格式如下:“This is a [test] sentence for [demonstration] purpose”。

应该显示为空输入字段的单词放在方括号中。这是它应该喜欢的:

我在计算属性的帮助下实现了这一点:

computed: {
    sentence_parts: function () {
        return this.sentence.split(/\[([^\]]*)\]/)
    }
}

并使用以下 HTML 代码:

<template>
    <div>
        <span v-for="(part, index) in sentence_parts" v-bind:key="index">
                <span v-if="index % 2 == 0">{{part}}</span>
                <span v-else><input type="text" v-bind:aria-colindex="index"></span>
        </span>
    </div>
</template>

基本上,我将字符串转换为数组,将具有偶数索引的句子部分放入一个跨度中,而将具有不均匀索引的句子部分替换为输入标签。

现在我的主要问题:实现允许将当前输入与“应该输入”(即句子数组中的输入)进行比较的功能的最佳方法是什么。

我能想出的唯一想法是让输入成为数组字段的模型,但问题是我不想用用户输入覆盖原始数组的条目,但我想保存它变成一个“单独的变量”,然后我可以从一个函数中访问它。

可能会起作用的一件事是创建一个长度为原始数组一半的新数组,将它们绑定到相应的字段,然后通过将其乘以 2 来计算它在原始数组中的位置(用于比较),但我想知道是否有更有效的方法。

(对于上下文,我正在开发一个允许用户测试他们的语法知识的应用程序。)

提前致谢!

【问题讨论】:

    标签: javascript vue.js


    【解决方案1】:

    这是我的尝试:

    const FillInTheBlanks = {
      template: `
        <div>
          <span v-for="(part, index) in sentenceParts" :key="index">
            <input
              v-if="part.input"
              v-model="part.guess"
              :aria-colindex="index"
              :class="{ correct: partIsCorrect(part) }"
            >
            <span v-else>{{ part.text }}</span>
          </span>
          <p v-if="allCorrect">All done!</p>
        </div>
      `,
    
      props: { sentence: String },
      
      data () {
        return {
          sentenceParts: []
        }
      },
    
      computed: {
        allCorrect () {
          return this.sentenceParts.every(this.partIsCorrect)
        }
      },
    
      methods: {
        partIsCorrect (part) {
          return !part.input || part.text === part.guess
        },
      
        reset () {
          const re = /(\[[^\]]*\])/
    
          // The filter removes empty strings
          const parts = this.sentence.split(re).filter(text => text)
    
          this.sentenceParts = parts.map(segment => {
            const isInput = re.test(segment)
    
            return {
              guess: '',
              input: isInput,
              text: isInput ? segment.slice(1, -1) : segment
            }
          })
        }
      },
      
      watch: {
        sentence: {
          immediate: true,
          handler: 'reset'
        }
      }
    }
    
    new Vue({
      el: '#app',
      
      components: {
        FillInTheBlanks
      },
      
      data () {
        return {
          sentence: 'This is a [test] sentence for [demonstration] purposes'
        }
      }
    })
    .correct, p {
      background: #7c7;
    }
    <script src="https://unpkg.com/vue@2.6.10/dist/vue.js"></script>
    <div id="app">
      Input sentence: <input v-model="sentence" style="width: 400px">
      <br>
      <br>
      <fill-in-the-blanks :sentence="sentence"></fill-in-the-blanks>
    </div>

    问题中描述的方法似乎完全合法,我只是想探索一种替代方法。

    最初我打算有两个数据结构,一个计算属性(如问题中所示)和data 中的一些东西来保存用户输入。但如果sentence 属性发生变化,我也想清除用户输入。为此,我需要使用watchdata 中删除相关属性。一旦我有了watch,就似乎更容易完全放弃计算属性并将所有内容放在data中的一个数组中。

    显然使用watch 代替计算属性会设置警钟响起。然而:

    1. 替代方案仍然需要watch
    2. 改变计算属性的输出绝对不是可行的选择。
    3. 试图使两个数据结构保持同步会附加其自身的健康警告。

    所以我决定摆脱计算属性是可用的妥协选项中最不坏的。根据未来的需求,这个决定可能需要重新考虑,但目前似乎可以降低复杂性。

    我尝试不使用偶数/奇数假设来确定是否使用&lt;input&gt;。具体来说,我想在开头处理&lt;input&gt;,尽管可以说这可以通过在开头放置一个虚拟空间来完成。我还希望它能够处理连续的输入,尽管在实践中这似乎不太必要,因为我想它们之间总是至少有一个空间。

    【讨论】:

    • 这很好用,谢谢。 :) 我需要寻找什么来理解重置部分中的 => 部分?这是我唯一不太明白的部分。
    • @Vallaris 这只是一个 ES6 箭头函数,developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…。问题中没有足够的代码让我知道您喜欢哪个版本,所以我使用了各种 ES6 语法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-21
    • 1970-01-01
    • 2017-04-20
    相关资源
    最近更新 更多