vuetify icon indicating copy to clipboard operation
vuetify copied to clipboard

fix(VNumberInput): avoid showing NaN

Open J-Sek opened this issue 8 months ago • 0 comments

Description

In order to avoid NaN we need to decouple "text" model for VTextField and the "exposed" model representing value for VNumberInput's consumer. I don't see it any other way and this approach served me many years in my custom wrapper arount VTextField back in Vuetify 2, so I am pretty sure it's the only way forward.

Each time we sync between these two

  • "text" model becomes valid number or an empty string – thanks to typeof model.value === 'number' && !isNaN(...)
  • "exposed" model becomes valid number – thanks to custom aggressive parsing in extractNumber(...)

Note: there are many ways to break this implementation, so we will need a bunch of test cases as a safety net for future changes. I plan on introducing them in the following days.

fixes 19798

Markup:

<template>
  <v-app theme="dark">
    <v-container>
      <v-card class="mx-auto pa-6 my-4" style="max-width: 1200px">
        <v-row>
          <v-col cols="4">
            <small class="d-block mb-2">(clearable)</small>
            <v-number-input v-model="emptyValue" clearable />
            <code>value: {{ emptyValue }}</code>
          </v-col>
          <v-col cols="4">
            <small class="d-block mb-2">(:step=".25")</small>
            <v-number-input v-model="value1" :step=".25" />
            <code>value: {{ value1 }}</code>
          </v-col>
          <v-col cols="4">
            <small class="d-block mb-2">(:max="20" :min="-10")</small>
            <v-number-input v-model="value2" :max="20" :min="-20" />
            <div class="d-flex align-center justify-space-between">
              <code>value: {{ value2 }}</code>
              <div class="text-right">
                <v-btn @click="value2 = 5.125">Set to 5.125</v-btn>
                <v-btn @click="value2 = NaN">Set to NaN</v-btn>
              </div>
            </div>
          </v-col>
        </v-row>
      </v-card>
    </v-container>
  </v-app>
</template>

<script setup lang="ts">
  import { ref } from 'vue'

  const emptyValue = ref<number>(null)
  const value1 = ref<number>(15)
  const value2 = ref<number>(-10.25)
</script>

J-Sek avatar May 27 '24 22:05 J-Sek