How to apply min/max attribute to v-model in Vue?

19,626

Solution 1

You could add a custom modifier to the v-model directive:

// function that gets the min and max values for the element and prevents the value of the model from going below the min or above the max
function bindNumberInRange(el, binding, vnode) {
  let model = binding.expression;
  let min = parseInt(el.min);
  let max = parseInt(el.max);
  let val = parseInt(binding.value);

  if ((min !== NaN) && (min >= val)) {
    vnode.context[model] = min;
  } else if ((max !== NaN) && (max <= val)) {
    vnode.context[model] = max;
  }

  el.value = val;
}

// get the original reference to the v-model directive
let modelDirective = Vue.directive('model')

// set a new definition of the v-model directive
Vue.directive('model', {
  bind: function(el, binding, vnode) { 
    // first fire the original v-model bind hook
    modelDirective.bind(el, binding, vnode);

    if (binding.modifiers.range) {
      bindNumberInRange(el, binding, vnode)
    }
  },
  componentUpdated: function(el, binding, vnode) {
    // first fire the original v-model componentUpdated hook
    modelDirective.componentUpdated(el, binding, vnode);

    if (binding.modifiers.range) {
      bindNumberInRange(el, binding, vnode)
    }
  }
})

Then, all you would need to do is add a .range modifier to v-model when you want the model to respect the min and max attributes of the affected element:

<input type="number" min="4" max="10" v-model.range="foo">

Here's a CodePen Example.

Here's Vue's (semi-lacking) documentation on directives.

Solution 2

Use @Focus to apply a max & min number validation to your :rules. Make sure to set the max with large default number. Both max & min default will be updated upon focusing on that specific input box. (This text-fields are being created with a loop)

<v-text-field type="number" 
  @focus="validateWhenFocused(item)"
  :rules="numberRules"
  :label="item.questionName"
  v-model="item.value"
  outline>
</v-text-field>

export default {
  data() {
  return {
    numberRules: [
    v => !!v || "Input is required!",
    v =>
      v < this.maxLength ||
      `${this.errorName} must be less than ${this.maxLength} numbers`,
    v =>
      v > this.minLength ||
      `${this.errorName} must be greater than ${this.minLength} numbers`
    ],
    maxLength: 100,
    minLength: 0,
    errorName: "",
  },
  methods: {
    validateWhenFocused(item){
    this.maxLength = item.maxValue
    this.minLength = item.minValue;
    this.errorName = item.questionName
  
    }
  }
}
Share:
19,626
Édouard Lopez
Author by

Édouard Lopez

Motivation I'm a quality-driven full-stack developer loving UX and devOps. Workflow include TDD and tests ; pair-programming ; code-review ; Trunk-based development ; Agile related. Languages and Tools Frontend: JS, ES6+, ReactJS, Vue.js, EmberJS, Angular, webpack/rollup ; test: unit Jest, AVA, end-to-end Cypress Backend: Python, Flask, Django, Unittest and Pytest ; DevOps: Docker/compose/Docker.py, Ansible ; Shell: bash/sh, fish, powershell test: Bats, fishtape, Pester Bonus I'm familiar with: UX (User eXperience) ; Accessibility ; Data-visualization ; IA (Information Architecture).

Updated on June 26, 2022

Comments

  • Édouard Lopez
    Édouard Lopez almost 2 years

    Current constraint of min and max are not respected due to the way v-on is implemented:

    <input id="passwordLength"
           class="form-control form-control-sm"
           type="number"
           min="5"
           max="35"
           v-model="options.length">
    <span class="input-group-btn" v-on:click="options.length+=1">
      <button class="btn btn-secondary" type="button">
        <i class="fa fa-plus"></i>
      </button>
    </span>
    

    Question

    How can I respect the constrain and still keep an elegant implementation?