9

<textarea name="" id="" cols="30" rows="10" v-model="$store.state.user.giftMessage | truncate 150"></textarea> 我尝试创建一个自定义过滤器:

filters: {
    truncate(text, stop, clamp) {
        return text.slice(0, stop) + (stop < text.length ? clamp || '...' : '')
    }
}

但是当我把它放在 v-model 上作为输入时,这并没有破坏构建......

有什么建议吗?

4

8 回答 8

10

这是您真正想要使用组件的情况之一。

这是一个呈现 a并限制文本数量的示例组件。textarea

请注意:这不是生产就绪的,处理所有角落案例组件。它旨在作为示例。

Vue.component("limited-textarea", {
  props:{
    value:{ type: String, default: ""},
    max:{type: Number, default: 250}
  },
  template: `
    <textarea v-model="internalValue" @keydown="onKeyDown"></textarea>
  `,
  computed:{
    internalValue: {
      get() {return this.value},
      set(v){ this.$emit("input", v)}
    }
  },
  methods:{
    onKeyDown(evt){
      if (this.value.length >= this.max) {
        if (evt.keyCode >= 48 && evt.keyCode <= 90) {
          evt.preventDefault()
          return
        }
      }
    }
  }
})

v-model如果文本的长度小于指定的最大值,该组件实现并且仅发出对数据的更改。keydown如果文本的长度等于或大于允许的最大值,它通过侦听并阻止默认操作(键入字符)来做到这一点。

console.clear()

Vue.component("limited-textarea", {
  props:{
    value:{ type: String, default: ""},
    max:{type: Number, default: 250}
  },
  template: `
    <textarea v-model="internalValue" @keydown="onKeyDown"></textarea>
  `,
  computed:{
    internalValue: {
      get() {return this.value},
      set(v){ this.$emit("input", v)}
    }
  },
  methods:{
    onKeyDown(evt){
      if (this.value.length >= this.max) {
        if (evt.keyCode >= 48 && evt.keyCode <= 90) {
          evt.preventDefault()
          return
        }
      }
    }
  }
})

new Vue({
  el: "#app",
  data:{
    text: ""
  }
})
<script src="https://unpkg.com/vue@2.4.2"></script>
<div id="app">
  <limited-textarea v-model="text" 
                    :max="10"
                    cols="30"
                    rows="10">
  </limited-textarea>
</div>

问题中代码的另一个问题是 Vuex 不允许您直接设置状态值;你必须通过突变来做到这一点。也就是说,应该有一个接受新值并设置它的 Vuex 突变,并且代码应该提交突变。

mutations: {
  setGiftMessage(state, message) {
    state.user.giftMessage = message
  }
}

在你的 Vue 中:

computed:{
  giftMessage:{
    get(){return this.$store.state.user.giftMessage},
    set(v) {this.$store.commit("setGiftMessage", v)}
  }
}

从技术上讲,代码应该使用 agetter来获取用户(它是 giftMessage),但这应该可以工作。在您将使用的模板中:

<limited-textarea cols="30" rows="10" v-model="giftMessage"></limited-textarea>

这是一个使用 Vuex 的完整示例。

console.clear()

const store = new Vuex.Store({
  state:{
    user:{
      giftMessage: "test"
    }
  },
  getters:{
    giftMessage(state){
      return state.user.giftMessage
    }
  },
  mutations:{
    setGiftMessage(state, message){
      state.user.giftMessage = message
    }
  }
})



Vue.component("limited-textarea", {
  props:{
    value:{ type: String, default: ""},
    max:{type: Number, default: 250}
  },
  template: `
    <textarea v-model="internalValue" @keydown="onKeyDown"></textarea>
  `,
  computed:{
    internalValue: {
      get() {return this.value},
      set(v){ this.$emit("input", v)}
    }
  },
  methods:{
    onKeyDown(evt){
      if (this.value.length >= this.max) {
        if (evt.keyCode >= 48 && evt.keyCode <= 90) {
          evt.preventDefault()
          return
        }
      }
    }
  }
})

new Vue({
  el: "#app",
  store,
  computed:{
    giftMessage:{
      get(){ return this.$store.getters.giftMessage},
      set(v){ this.$store.commit("setGiftMessage", v)}
    }
  }
})
<script src="https://unpkg.com/vue@2.4.2"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/2.4.0/vuex.js"></script>
<div id="app">
  <limited-textarea v-model="giftMessage" 
                    :max="10"
                    cols="30"
                    rows="10">
  </limited-textarea>
  Message: {{giftMessage}}
</div>

于 2017-09-19T00:33:32.093 回答
8

抱歉闯入。正在寻找解决方案。看着他们所有人。对我来说,它们看起来太复杂了。我一直在寻找简单。因此,我喜欢@Даниил Пронин 的回答。但它有@J。兰博指出了潜在的问题。

尽可能接近原生 html 文本元素。我想出的解决方案是:

Vue 模板

<textarea v-model="value" @input="assertMaxChars()">

JavaScript

let app = new Vue({
  el: '#app',
  data: {
    value: 'Vue is working!',
    maxLengthInCars: 25
  },

  methods: {
    assertMaxChars: function () {
        if (this.value.length >= this.maxLengthInCars) {
            this.value = this.value.substring(0,this.maxLengthInCars);
        }
    }
  }
})

这是一个 REPL 链接:https ://repl.it/@tsboh/LimitedCharsInTextarea

我看到的好处是:

  • 元素尽可能接近原生元素
  • 简单的代码
  • textarea 保持焦点
  • 删除仍然有效
  • 也适用于粘贴文本

无论如何快乐编码

于 2020-05-08T18:58:02.467 回答
3

虽然我同意所选答案。您还可以使用 keydown 事件处理程序轻松防止长度。

Vue 模板

<input type="text" @keydown="limit( $event, 'myModel', 3)" v-model="myModel" />

JavaScript

export default {
    name: 'SomeComponent',

    data () {
        return {
            myModel: ''
        };
    },

    methods: {

        limit( event, dataProp, limit ) {
            if ( this[dataProp].length >= limit ) {
               event.preventDefault();
            }
        }
    }
}

这样做,您还可以使用正则表达式来阻止接受的键类型。例如,如果您只想接受数值,您可以执行以下操作。

methods: {
   numeric( event, dataProp, limit ) {
       if ( !/[0-9]/.test( event.key ) ) {
           event.preventDefault();
       }
   }
} 
于 2018-05-31T14:59:32.170 回答
2

我已经改进了@J Ws 的答案。生成的代码不必定义如何对哪个按键做出反应,这就是为什么它可以与任何字符一起使用,而不是接受的答案。它只关心结果的字符串长度。它还可以处理复制粘贴操作并将过长的粘贴剪切成一定大小:

Vue.component("limitedTextarea", {
    props: {
      value: {
        type: String,
        default: ""
      },
      max: {
        type: Number,
        default: 25
      }
    },
    computed: {
      internalValue: {
        get: function () {
          return this.value;
        },
        set: function (aModifiedValue) {
          this.$emit("input", aModifiedValue.substring(0, this.max));
        }
      }
    },
    template: '<textarea v-model="internalValue" @keydown="$forceUpdate()" @paste="$forceUpdate()"></textarea>'
});

神奇之处在于@keydown 和@paste-events,它们会强制更新。由于该值已经被正确地切割成大小,它确保 internalValue 相应地起作用。

如果您还想保护该值不受未经检查的脚本更改的影响,您可以添加以下观察程序:

watch: {
  value: function(aOldValue){
    if(this.value.length > this.max){
      this.$emit("input", this.value.substring(0, this.max));
    }
  }
}

我刚刚发现这个简单解决方案的一个问题:如果将光标设置在中间某处并键入,超过最大值,则删除最后一个字符并将光标设置到文本的末尾。所以还是有一些改进的空间...

于 2018-10-16T08:41:51.333 回答
1

我的自定义指令版本。使用简单。

<textarea v-model="input.textarea" v-max-length="10"></textarea>


Vue.directive('maxlength',{
    bind: function(el, binding, vnode) {
        el.dataset.maxLength = Number(binding.value);
        var handler = function(e) {
            if (e.target.value.length > el.dataset.maxLength) {
                e.target.value = e.target.value.substring(0, el.dataset.maxLength);
                var event = new Event('input', {
                    'bubbles': true,
                    'cancelable': true
                });
                this.dispatchEvent(event);
                return;
            }
        };
        el.addEventListener('input', handler);
    },
    update: function(el, binding, vnode) {
        el.dataset.maxLength = Number(binding.value);
    }
})
  1. Event() 存在浏览器兼容性问题。
  2. 对我来说不幸的是,keydown 方法似乎不适用于 CJK。
  3. 由于此方法会两次触发输入事件,因此可能会产生副作用。
于 2018-10-29T12:49:33.970 回答
0

我使用了您的代码并将其分解为 .Vue 组件,谢谢! <template> <textarea v-model="internalValue" @keydown="onKeyDown"></textarea> </template>

`

export default {
    props:{
        value:{ type: String, default: ""},
        max:{type: Number, default: 250}
    },
    computed:{
        internalValue: {
            get() {return this.value},
            set(v){ this.$emit("input", v)}
        }
    },
    methods:{
        onKeyDown(evt){
            if (this.value.length >= this.max) {
                evt.preventDefault();
                console.log('keydown');
                return
            }
        }
    }

}

于 2017-09-19T13:09:14.043 回答
0

最好的方法是使用 watch 来字符串长度,如果字符串比你想要的长,则设置旧值:

watch: {
    'inputModel': function(val, oldVal) {
        if (val.length > 250) {
            this.inputModel = oldVal
        }
    },
},
于 2018-04-28T06:27:05.653 回答
0

只需使用maxlength如下属性:

<textarea v-model="value" maxlength="50" />
于 2021-07-16T17:58:29.613 回答