【问题标题】:Vue : Limit characters in text area input, truncate filter?Vue:限制文本区域输入中的字符,截断过滤器?
【发布时间】:2018-02-27 13:58:14
【问题描述】:

<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 上作为输入时,这并没有破坏构建......

有什么建议吗?

【问题讨论】:

    标签: vue.js vuejs2 truncate v-model


    【解决方案1】:

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

    这是一个示例组件,它呈现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)}
      }
    }
    

    从技术上讲,代码应该使用getter 来获取用户(它是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>

    【讨论】:

    • 我最终用你的代码把它分解成一个 .Vue 组件:
    • `
    • 这不起作用,因为当您达到限制时,您不能再删除字符或添加字符。
    • 另请注意,对于 keyCode,仍然会添加不是键 1 到 0 或字母 a-z 的字符(范围 48 -> 90 之外的任何字符),真的
    【解决方案2】:

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

    尽可能接近原生 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 保持焦点
    • 删除仍然有效
    • 也适用于粘贴文本

    无论如何 快乐编码

    【讨论】:

      【解决方案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();
             }
         }
      } 
      

      【讨论】:

        【解决方案4】:

        我已经改进了@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));
            }
          }
        }
        

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

        【讨论】:

          【解决方案5】:

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

          <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. 可能会有副作用,因为此方法会两次触发输入事件。

          【讨论】:

            【解决方案6】:

            我使用了您的代码并将其分解为 .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
                        }
                    }
                }
            
            }
            

            【讨论】:

              【解决方案7】:

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

              watch: {
                  'inputModel': function(val, oldVal) {
                      if (val.length > 250) {
                          this.inputModel = oldVal
                      }
                  },
              },
              

              【讨论】:

              • 此解决方案的问题是您正在修改该属性观察器中的一个观察属性。虽然该条件可能会阻止无限递归的发生,但它仍然是一种不鼓励的做法。
              【解决方案8】:

              像这样简单地使用maxlength属性:

              <textarea v-model="value" maxlength="50" />
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2011-07-28
                • 1970-01-01
                • 2020-03-15
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2017-12-16
                相关资源
                最近更新 更多