1

问题

我试图解决一个 VueJs 挑战。这个挑战有一组单元测试来验证我的代码的正确性。其中一些测试围绕输入文本字段进行。我按照通常的方式使用创建自定义输入组件,v-model但检查输入验证逻辑的测试失败。

安装组件时,v-model初始值为""(空字符串)。然后测试使用设置一个值setValue,然后使用检查输入是否具有正确的值customInput.element.value

const app = mount(App);
const customInput = app.find(".custom-input-field-a");

customInput.setValue("invalid input");
expect(customInput.element.value).toEqual("");

setValue语句之后,输入元素触发一个input事件,由自定义组件处理。这个处理程序验证输入文本,然后发出一个input事件来更新 v-model。

发生这种情况时,不会更新任何内容,因为前一个值和下一个值等于""(此处没有“反应性”),因此语句customInput.element.value返回"invalid input"

代码

这是自定义组件:

<template>
  <div class="custom-input">
    <label :class="labelClasses" :for="name">{{ label }}</label>
    <input
      :class="`custom-input-${name}`"
      :name="name"
      :maxlength="3"
      type="text"
      :value="value"
      @input="onInput"
      @keypress="onlyLettersFilter"
    />
  </div>
</template>

<script>
export default {
  props: {
    name: { type: String, required: true },
    placeholder: { type: String, default: "some placeholder" },
    label: String,
    value: String,
  },
  computed: {
    hasValue() {
      return this.value?.length > 0;
    },
    labelClasses() {
      return "custom-input--label " + (this.hasValue ? "active" : "");
    },
  },
  methods: {
    filterValue(input) {
      let value = input.slice(0, 3);
      value = value.toUpperCase().replace(/([^A-Z])+/g, "");
      return value;
    },
    onInput(event) {      
      this.$emit("input", this.filterValue(event.target.value));
    },
    onlyLettersFilter(event) {
      if (event.keyCode === 13) return true;
      var key = String.fromCharCode(
        !event.charCode ? event.which : event.charCode
      );
      // Accept only letters
      var regex = new RegExp(/[a-zA-Z]+/);
      if (regex.test(key)) {
        return true;
      }
      event.preventDefault();
      return false;
    },
  },
};
</script>

<style>
</style>

在这里您可以看到,在任何输入更改中,文本都会被过滤,删除不需要的字符。如果所有字符都无效,则结果为空字符串。

解决方法

作为一种解决方法,我设置了对输入元素的引用并直接更新了内容,而不使用 VueJs 功能。所有测试都通过了。

<template>
  ...
    <input
      ...
      ref="inputRef"
      ...
    />
  ...
</template>
<script>

export default {
  ...
  methods: {
    ...
    
    onInput(event) {
      const value = this.filterValue(event.target.value);
      // this prevents any invalid character in the input element when the model value doesn't change, when seting the value in unit tests
      this.$refs.inputRef.value = value;
      if (value === this.value) event.preventDefault();
      else {
        this.$emit("input", value);
      }
    },
  }
  ...
}
</script>

我知道这是一个糟糕的解决方案,但我不知道更好。

在不使用 ref 和直接修改 HTML 组件的情况下,是否有更好、更简洁的方法来做到这一点?

4

0 回答 0