Vue.JS 与具有焦点的输入相关联的值

Vue.JS value tied on input having the focus

有没有办法在输入 gets/loses 焦点时更改模型中的值?

此处的用例是在您键入时显示结果的搜索输入,这些应该仅在焦点位于搜索框上时显示。

这是我目前的情况:

<input type="search" v-model="query">
<div class="results-as-you-type" v-if="magic_flag"> ... </div>

然后,

new Vue({
  el: '#search_wrapper',
  data: {
    query: '',
    magic_flag: false
  }
});

这里的想法是,当搜索框获得焦点时,magic_flag 应该变为 true。我可以手动执行此操作(例如,使用 jQuery),但我想要一个纯粹的 Vue.JS 解决方案。

显然,这就像在 event handlers 上编写一些代码一样简单。

<input 
  type="search" 
  v-model="query"
  @focus="magic_flag = true"
  @blur="magic_flag = false"
/>
<div class="results-as-you-type" v-if="magic_flag"> ... </div>

您可能还想在用户将鼠标悬停在输入上时激活搜索 - @mouseover=...

这种功能的另一种方法是过滤器输入始终处于活动状态,即使鼠标位于结果列表中也是如此。键入任何字母都会在不改变焦点的情况下修改过滤器输入。许多实现实际上仅在键入字母或数字后才显示过滤器输入框。

查看@event.capture.

在更复杂的情况下处理此类问题的另一种方法可能是允许表单跟踪当前处于活动状态的字段,然后使用观察器。

我将展示一个快速示例:

<input
    v-model="user.foo"
    type="text"
    name="foo"
    @focus="currentlyActiveField = 'foo'"
>

<input
    ref="bar"
    v-model="user.bar"
    type="text"
    name="bar"
    @focus="currentlyActiveField = 'bar'"
>

...

data() {
    return {
        currentlyActiveField: '',
        user: {
            foo: '',
            bar: '',
        },
    };
},

watch: {
    user: {
        deep: true,
        handler(user) {
            if ((this.currentlyActiveField === 'foo') && (user.foo.length === 4)) {
                // the field is focused and some condition is met
                this.$refs.bar.focus();
            }
        },
    },
},

在我这里的示例中,如果当前活动字段是 foo 并且值是 4 个字符长,那么下一个字段 bar 将自动聚焦。这种类型的逻辑在处理包含信用卡号、信用卡有效期和信用卡安全代码输入等内容的表单时很有用。可以通过这种方式改进用户体验。

希望这能激发您的创造力。观察者很方便,因为它们允许您监听数据模型的更改,并在触发观察者时根据您的自定义需求采取行动。

在我的示例中,您可以看到每个输入都已命名,并且组件知道当前关注的是哪个输入,因为它正在跟踪 currentlyActiveField

我展示的观察器有点复杂,因为它是一个“深度”观察器,这意味着它能够观察对象和数组。如果没有 deep: true,观察者只会在 user 被重新分配时被触发,但我们不希望那样。我们正在 user.

上查看键 foobar

在幕后,deep: true 正在向 this.user 上的所有键添加观察者。在没有启用 deep 的情况下,Vue 合理地不会产生响应式维护每个键的成本。

一个简单的观察者会是这样的:

watch: {
    user() {
        console.log('this.user changed');
    },
},

注意:如果你发现我有 handler(user) { 的地方,你可能有 handler(oldValue, newValue) { 但你注意到两者显示相同的值,这是因为两者都是对同一个 [=19] 的引用=] 对象。在这里阅读更多:https://github.com/vuejs/vue/issues/2164

Edit: to avoid deep watching, it's been a while, but I think you can actually watch a key like this:

watch: {
    'user.foo'() {
        console.log('user foo changed');
    },
},

但如果这不起作用,你也可以制作一个计算道具然后观看:

computed: {
    userFoo() {
        return this.user.foo;
    },
},

watch: {
    userFoo() {
        console.log('user foo changed');
    },
},

我添加了这两个额外的示例,以便我们可以快速注意到深度监视会消耗更多资源,因为它触发得更频繁。我个人避免深入观察,而是在合理的情况下进行更精确的观察。

然而,在这个带有 user 对象的示例中,如果所有键都对应于输入,那么深度监视是合理的。也就是说可能是。

您可以通过确定一个特殊的 CSS class 来使用平面,例如这个简单的片段:

var vm = new Vue({
 el: '#app',
 data: {
  content: 'click to change content',
  flat_input_active: false
 },
 methods: {
  onFocus: function(event) {
          event.target.select();
          this.flat_input_active = true;
  },
  onBlur: function(event) {
          this.flat_input_active = false;
  }
 },
 computed: {
  clazz: function() {
   var clzz = 'control-form';
   if (this.flat_input_active == false) {
    clzz += ' only-text';
   }
   return clzz;
  }
 }
});
#app {
  background: #EEE;
}
input.only-text { /* special css class */
  border: none;
  background: none;
}
<!-- libraries -->
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>

<!-- html template -->
<div id='app'>
 <h1>
 <input v-model='content' :class='clazz'
  @focus="onFocus($event)"
  @blur="onBlur"/>
 </h1>
<div>

祝你好运