widgetti / ipyvuetify

Jupyter widgets based on vuetify UI components
MIT License
348 stars 59 forks source link

TextField with `type='number'` - decimal separator and validation #241

Open Jhsmit opened 1 year ago

Jhsmit commented 1 year ago

Hi, I've just started out with ipyvuetify and I'm looking for how to make a form for a user to input a float value.

I'm using this:

f = v.TextField(v_model = 0.2, type='number', attributes={'min': 0, 'max': 1, 'step': 0.1})

The output is this: image

But when click the spinbox I get this: image

Why does the type and decimal separator change here? Is there something I can do to keep the separator . and the value of v_model a float?

Second, I'm trying to make it reject text or show some feedback if the value entered cannot be converted to a float. I don't have JS experience so I've scrounged the issue pages and tried the code below, but there is something with the :rules part that doesnt work.

class MyNumberField(v.VuetifyTemplate):
    label = traitlets.Unicode().tag(sync=True)
    value = traitlets.Unicode(default_value='0').tag(sync=True)
    min = traitlets.Float(default_value=0).tag(sync=True)
    max = traitlets.Float(default_value=1).tag(sync=True)
    step = traitlets.Float(default_value=1).tag(sync=True)

    temp = traitlets.Float()
    temp_error = traitlets.Unicode(default_value=None)

    @traitlets.default('template')
    def _template(self):
        return '''
          <v-text-field
            :label="label"
            type="number"
            v-model="value"
            @change="temp_rule"
            :rules="[temp_error]"
            :min="min" :max="max" :step="step"
            @input="input($event)"
          ></v-text-field>
        '''

    def vue_temp_rule(self, v):
        self.temp_error = None
        try:
            self.temp = float(v)
        except:
            self.temp_error = 'testtest'

    def vue_input(self, data):
        print(data)

mf = MyNumberField()
mf
Jhsmit commented 1 year ago

The rules part works now, I realized I was missing a tag(sync=True).

I'm still curious if the decimal separator can be forced to always be a point?

Updated code:

class MyNumberField(v.VuetifyTemplate):
    label = traitlets.Unicode().tag(sync=True)
    value = traitlets.Unicode(default_value='0').tag(sync=True)
    min = traitlets.Float(default_value=0).tag(sync=True)
    max = traitlets.Float(default_value=1).tag(sync=True)
    step = traitlets.Float(default_value=0.1).tag(sync=True)

    temp = traitlets.Float().tag(sync=True)
    temp_error = traitlets.Any(default_value=None).tag(sync=True)

    @traitlets.default('template')
    def _template(self):
        return '''
          <v-text-field
            :label="label"
            type="number"
            v-model="value"
            @change="temp_rule"
            :rules="[temp_error]"
            :min="min" :max="max" :step="step"
            @input="input($event)"
          ></v-text-field>
        '''

    def vue_temp_rule(self, v):
        self.temp_error = None
        try:
            self.temp = float(v)
        except:
            self.temp_error = 'Input value must be a number'

    def vue_input(self, data):
        print(data)

mf = MyNumberField()
12rambau commented 1 year ago

I know I'm late to the party but reading this mozilla documentation, I have the feeling that the separator used by the vuetify widget is bound to the locale of your browser.

@Jhsmit, have ou manage to find a solution on you side ?

Jhsmit commented 1 year ago

That sounds plausible as I'm having similar problems with the decimal separator if I would use excel (silly dutch use comma as decimal separator)

I have found a 'solution' in the sense that I'm not using type="number" anymore. This solves my problem because the spin buttons are now gone, and I didn't like them anyway. See also this article on stackoverflow

I'm now using solara to wrap their InputText, which supports passing error messages. See for example this RangeInputField