Vue: 添加自定义 v-model 修饰符

创建于 2016-09-13  ·  52评论  ·  资料来源: vuejs/vue

我们有.lazy.number.trim.undef即将推出。

除了.lazy之外,它们都像双向过滤器一样工作。

由于 2.0 不支持 2 路过滤器,可能应该有一个新的 api 来添加自定义 v-model 修饰符以满足相同的需求。

feature request

最有用的评论

我们有机会重新讨论这个问题吗? 对我来说,一个常见的用例是需要在输入字段时自动格式化数据。例如将“101216”转换为“10/12/16”。 能够创建自定义 v-model 修饰符将大大简化我的代码,因为我可以编写 v-model.date 而不必使用道具和事件构建自定义输入组件。

所有52条评论

@posva计算属性不可重用。

几乎所有东西都可以通过 mixin 重用。
您可以使用生成 mixin 的函数。 这样你就可以绑定一个
动态计算的属性。 我现在不能把这个例子放在小提琴上
但我会尽快做。
但是,我同意这是一个非常常见的输入用例
应用的转换。 一个适当的 api 或至少一个解释
指南是必要的

2016 年 9 月 13 日,星期二,18:48,Francisco Lourenço, notifications @github.com
写道:

@posva https://github.com/posva计算属性不可重用。


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/vuejs/vue/issues/3666#issuecomment -246746524,或者静音
线程
https://github.com/notifications/unsubscribe-auth/AAoicf33lCvETQc9LBQ5GGZ93ExPcLS_ks5qptPegaJpZM4J7vQ0
.

几乎所有东西都可以通过 mixin 重用。
您可以使用生成 mixin 的函数。 这样你就可以绑定一个
动态计算的属性。 我现在不能把这个例子放在小提琴上
但我会尽快做。
但是,我同意这是一个非常常见的输入用例
应用的转换。 一个适当的 api 或至少一个解释
指南是必要的

换句话说,计算属性是不可重用的。 您可以使用工厂函数 + mixins 作为解决方法,但可用性和可读性无法比较。

对于我的项目,我非常需要这个功能,所以我使用了推荐的自定义输入方法:

InputCustom.js

define(function () {
  return Vue.extend({
    data: function () {
      return {
        focused: false
      };
    },
    template: '<input @focus="onFocus" @blur="onBlur" @input="onInput" @change="setDisplayValue">',
    props: ['value'],
    watch: {
      value: function () {
        if (!this.focused) {
          this.setDisplayValue();
        }
      }
    },
    mounted: function () {
      this.setDisplayValue();
    },
    methods: {
      onInput: function () {
        this.$emit('input', this.parse(this.$el.value));
      },
      onFocus: function () {
        this.focused = true;
      },
      onBlur: function () {
        this.focused = false;
        this.setDisplayValue();
      },
      setDisplayValue: function () {
        this.$el.value = this.format(this.value);
      }
    }
  });
});

输入文本.js

define(['js/InputCustom'], function (InputCustom) {
  return InputCustom.extend({
    methods: {
      parse: function (val) {
        val = val.trim();
        return val === '' ? null : val;
      },
      format: function (val) {
        return val === null ? '' : val;
      }
    }
  });
});

在我看来,这种方法非常方便,我决定不使用任何 v-model 修饰符,包括.lazy

对于内置修饰符无法支持的更多自定义用例, @ecmel提到的是推荐的方法。 我们将在官方指南中更详细地记录这一点。

此功能提案的想法是利用现有的v-model指令,该指令已经适用于每个input元素。 为了节省在每个项目中编写 _InputCustom.js_ 的工作,因为这已经在v-model中完成了,只需在自定义修饰符中编写相当于 _InputText.js_ 的内容,其中包含需要的所有逻辑大部分时间都会被修改。 v-model已经附带修饰符的事实证明它是一种直观且理想的模式。 方便创建自定义修饰符,省去创建自定义元素和手动实现 dom/model 绑定的工作是很自然的。

如果从 API 的角度来看它是有意义的,那么了解哪些技术限制导致了不实现此功能的决定会很有趣。

我们有机会重新讨论这个问题吗? 对我来说,一个常见的用例是需要在输入字段时自动格式化数据。例如将“101216”转换为“10/12/16”。 能够创建自定义 v-model 修饰符将大大简化我的代码,因为我可以编写 v-model.date 而不必使用道具和事件构建自定义输入组件。

在我的项目中使用vue js一段时间后,我认为这个问题确实应该重新打开。

至少我们需要一个undef修饰符。

我同意这个问题应该重新打开。 不确定undef应该做什么,但我想要一个v-model修饰符,将我的变量设置为null ,以防输入的修剪值为空字符串。

我希望自己能够以一种直接的方式做到这一点。

例如https://github.com/vuejs/vue/issues/5194添加了比这更多的功能。 从外部看,Vue 似乎正在慢慢地妥协它的一些原则,以支持 React 社区提倡的约定和实践。 与最初使其脱颖而出的品质略有不同。 想知道这是一个有意识的决定,目的是让反应更容易迁移,还是只是巧合,会很有趣。

编写自定义组件很好,但是如果您想使用像https://github.com/text-mask/text-mask/tree/master/vue#readme这样的第 3 方自定义组件,则没有直接的方法来清理蒙面模型值的输入,但使用计算属性除外。

所以,我只想使用 HTML 标准 input[type=date] 字段来编辑我的模型中的日期类型,而这个功能强大且可扩展的框架不能开箱即用吗? 无法将日期读入字段,选择日期后用数据中的字符串覆盖我的日期。 这个解决方案可以用双向过滤器或修饰符写成两行。

但是最好的解决方案就是像对复选框和其他标准输入字段一样原生支持它,为什么“日期”是一个特殊的东西?

+1 用于自定义修饰符。 似乎很容易,除非有充分的理由不这样做?

屏蔽输入并解析应用程序使用的值是一种非常常见的做法,制作一些像 v-model.lazy.currency="amount" 这样的“语法糖”会很棒!

1+ 用于自定义修饰符。
我有一个带有 true|false 值的简单无线电输入,其值为字符串 - 但我需要将它们作为布尔值 - 在这种情况下,计算属性不会很聪明,因为我需要为每个无线电输入重新实现计算属性。 例如,拥有 100 个无线电输入将产生 100 个计算属性

+1 自定义修饰符,但我同意 tobei - input[type=date] 应该自动工作。

+1 用于自定义修饰符。

我来自 Angular 背景,刚开始使用 vue,看到了这个线程。

我觉得在 Vue 中拥有像 Angular 的解析器和格式化程序这样的东西真的很有帮助。 如果我可以做类似 v-model.dateFormat 的事情并产生类似 mm/dd/yyyy 的结果,那就太酷了。

编辑:看起来它重申了@restored18所说的内容。 也给你+1

+1 用于自定义 v-model 修饰符。

在我的例子中,我遍历了在 JSON 中检索到的几个嵌套对象,并使用单个 HTML 模板(而不是每个对象的模板)。 在这种情况下,我相信计算属性不起作用?

我目前在获取和发送数据时在服务器格式和 v-model 格式之间添加自定义转换方法,但我希望我可以将函数传递给“内置”的东西。

对此+1。 它曾经在 2.2 之前可用。 您可以通过以下方式访问该物业,

this.$vnode.data.directives

随着自定义模型输入值的添加,它被删除了,但它是一个非常有用的特性,应该回到框架中。

为此+1。

自定义 v-model 修改器会很棒!

我也是

+1 在 2018 年以及 ...

+1 在我看来,这是 DRY 代码的必要功能。 现在,我必须创建 10 个观察者,它们对具有大量输入的表单执行相同的操作。 自定义修饰符可以解决所有问题。

+1我刚开始使用vue并且已经需要这种类型的双向过滤器......

+1 绝对需要

+1

您可以为大多数用例 IMO 构建这样的助手

@nickmessing没有涵盖此处描述的(真正有用的)用例,这是对输入文本的就地修改。 如果您有一个希望始终像电话一样格式化的输入框,那么您将拥有<input v-model.phone="some_data"> 。 当用户输入文本时,它会自动格式化。

这似乎是一个基本功能,而且比现在更难。 该行为已经存在于框架中,但由于某种原因,它仅限于框架代码。 我们希望能够添加一个自定义修饰符来做到这一点,它可以跨组件和项目重用,就像现在的过滤器和指令一样。

@bbugh完全同意,我在格式化 IBAN 时遇到了类似的情况,只需将v-model.iban="payment.iban"放在那里,这将是一种很好的贬义方式......

@franciscolourenco也许有人可以解释为什么框架不应该支持它,以便它变得更加明显。

+1 自定义修饰符,有很多用例可以用这个功能完成

在我们的应用程序中,格式化货币的输入很少,我们总是在模型中存储美分金额,但在输入中显示格式化好的美元金额(因此模型中的 123456 显示为 $1,234.56) <input v-model.dollars="cents" />

其他用例是清理和取消转义 html 字段以防止 XSS 攻击(模型存储“ A &amp; B ”,而输入显示“ A & B ”) <input v-model.html="text" />

+1

+1

+1 用于自定义修饰符。
我真的很惊讶我不能做像v-model.trim.uppercase=...这样的事情

+1

+1

+1

+1
原生v-model输入修饰符将是一个很棒的功能。 正如人们在这里提到的那样,有很多用例。 我一直在从事的所有项目都需要日期和货币修饰符。

我们应该在这里打开一个问题吗? https://github.com/vuejs/rfcs

我已经为此 +1 了,但想给“现在”需要一些东西的人写个便条

虽然修饰符更有效,但我已经能够使用带有计算的 getter/setter 字段的透明输入/组件来实现相同的效果。

如果有人需要,我可以分享一个例子

我们没有实现这一点,因为在这个看似简单的功能中需要考虑很多事情:

  • 内置修饰符实际上是生成不同编译代码的编译时提示。 自定义修饰符可能需要使用运行时配置来定义,这是一种不同的机制。

  • 对于运行时配置,我们应该为此公开什么样的 API?

  • 过去我们有双向过滤器。 双向值转换需要用户实现无可挑剔的逻辑,以使双向绑定能够稳定。 否则,您可能会将整个应用程序置于无限循环中以应对极端情况。

    • 我们有.number.trim的原因是因为它们实际上是单向转换(仅在将 DOM 值同步到组件数据时应用),因此可以保证稳定。
  • 当在组件上使用v-model时,自定义修饰符应该如何表现?

  • 自定义修饰符应如何处理非文本输入类型,例如radiocheckbox和最重要的<select>

所有这些问题都没有得到解答,使请求比看起来更复杂。 这就是为什么我同意如果有人真的想要这个特性,它会是一个涵盖所有这些的适当 RFC 的一个很好的候选者。 在那之前,更多的 +1 不会以任何方式推动它前进。

@rkingon https://github.com/vuejs/vue/issues/3666#issuecomment -249583603 中已经有一个示例,但是如果您的不同/更好,请发布它。 它对新手很有用。

@Gotterbild我确实错过了那个示例,但是在 Vue 的更高版本中它非常复杂(这可能是预透明组件支持)

这是我拥有的一个非常简单的方法,它只是将百分比转换为小数(即:4.5 -> .045),反之亦然(“视图”值和“模型”值)

<template>
    <input type="number" v-on="listeners" v-model="innerValue">
</template>

<script>

    export default {
        props: ['value'],
        computed: {
            listeners () {
                const { input, ...listeners } = this.$listeners
                return listeners
            },
            innerValue: {
                get () {
                    return (this.value) ? `${(this.value * 100)}` : ''
                },
                set (_val) {
                    const val = (_val) ? (_val / 100) : ''
                    this.$emit('input', val)
                }
            }
        }
    }

</script>

这比上面更简单,因为您不必重新指定所有焦点/模糊/等

@yyx990803感谢您提供有关此主题的更多背景信息。

对于我的用例,我不需要“自定义”修饰符。 只是从消费者的角度来看,vue 有一种构建自己的方法是有道理的。 它基本上对所有东西都有,除了修饰符😄

虽然我可以找到方法来创建正确封装我的转换逻辑并且我可以重用的东西,但我认为为此类用例提供适当的 API 将开辟一种更广泛的方式,通过一组“最佳 -练习修饰符”。

我来到这里是因为我正在寻找将字符串转换为大写的修饰符
我想像v-model.uppercase="username"一样创建它

我最终使用了自定义指令

Vue.directive('uppercase', {
    update (el) {
        el.value = el.value.toUpperCase()
    },
})

<input type="text" v-model="username" v-uppercase>

自定义指令应该足以替代。
还是只有使用自定义 v-model 修饰符才有可能?

@Christhofernatalius考虑到v-model只是一个指令,你不能消除v-model以支持自定义指令吗? 这样你不会更新两次?

@Christhofernatalius考虑到v-model只是一个指令,你不能消除v-model以支持自定义指令吗? 这样你不会更新两次?

@rkingon是否更新了两次?
那么,如果我不使用 v-model,那么我还需要为输入侦听器添加绑定和取消绑定钩子,并更新用户名值吗?

编辑:使用 setter 和 getter 计算的不是也更新两次吗?
编辑2:我尝试控制台记录观察者和指令,每次击键只打印一次。

我没有尝试过,只是猜测——我想两个指令更新一个值的想法对我来说有点有趣,但如果你检查过,我看不出有什么问题。

我的组件解决方法也有局限性,这就是为什么我仍然支持修饰符 - 即:它需要一个元素,更多的渲染时间,并且只能作为该组件的定义方式的组件(即:输入字段) 甚至能够简单地在某些任意组件/元素上使用它,这是指令的力量。

两种剥猫皮的方法,很高兴有选择:)

有这方面的 RFC 吗?

我不这么认为

我可以创建它,但这是否只是意味着在该 RFC 存储库或其他内容中添加一个问题?

https://github.com/vuejs/rfcs#what -the-process-is 这能回答你的问题吗?

请问这个可以重开吗? 自定义指令在某些情况下可能有效,但行为可能很有趣,例如:

Vue.directive('number', {
  update: function(el, binding, vnode) {
    el.value = el.value.replace(/[^\d.]/g, '');
  },
});

如果您添加此指令(以及 v-model)并在每隔一次击键时快速键入字母,则v-model将与el.value不同步。 而且由于您无法修改收到的绑定对象,因此无法在此指令中“重新实现” v-model

@jeankvd我知道这感觉有点矫枉过正,但包装器组件将是最可靠的(参见上面的示例)。

包装器组件也将允许您“做更多”。 在您的“数字”示例中,理想情况下,v-model 实际上变成了Number 。 在您的方法中,它仍然是String

如果要自定义空值怎么办? 空字符串? 空值? 不明确的? -- 你可以为emptyValue传入一个 prop 并将其设置为这样。

虽然曾经是这一点的倡导者,但我很快意识到修饰符有太多的限制,而仅仅拥有一个组件就优越得多(至少在 imo 中)。

此页面是否有帮助?
0 / 5 - 0 等级