web-dev-qa-db-ja.com

Vue:テキスト領域入力の文字を制限し、フィルターを切り捨てますか?

<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モデルに配置しても、ビルドが壊れることはありませんでした...

何かアドバイス?

3
J W

これは、コンポーネントを本当に使用したい場合の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/[email protected]"></script>
<div id="app">
  <limited-textarea v-model="text" 
                    :max="10"
                    cols="30"
                    rows="10">
  </limited-textarea>
</div>

問題のコードのもう1つの問題は、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/[email protected]"></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>
7
Bert

@J Wsの回答を改善しました。結果のコードでは、どのキーを押したときの反応を定義する必要はありません。そのため、受け入れられた回答とは対照的に、どの文字でも使用できます。結果の文字列長のみが考慮されます。また、Copy-Paste-actionsを処理し、長すぎるペーストをサイズに合わせてカットすることもできます。

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));
    }
  }
}

私はこの簡単な解決策に問題を見つけました。カーソルを中央のどこかに設定し、最大値を超えて入力すると、最後の文字が削除され、カーソルがテキストの終わりに設定されます。したがって、まだ改善の余地があります...

2
Nils-o-mat

カスタムディレクティブのバージョン。使い方は簡単。

<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. 残念ながら、キーダウンアプローチはCJKではうまく機能しないようです。
  3. このメソッドは入力イベントを2回発生させるため、副作用が発生する可能性があります。
1
user1386890

選択した答えに同意します。 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();
       }
   }
} 
1
J. Rambo

私はあなたのコードを使用してそれを.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
            }
        }
    }

}
0
J W

最善の方法は、文字列の長さを監視して、文字列が必要以上に長い場合は古い値を設定することです。

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