Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A combobox that auto-corrects the value

I have a combobox in which one can select a certain time span, e.g.:

5 minutes
15 minutes
1 hour
2 hours
1 day
2 days
1 week
2 weeks

It always transmits the number of minutes to the server, but the user wouldn't understand what "10080" means (before you try to calculate: it's a week).

A new requirement is that the user should be able to type arbitrary values into that box. E.g. "20 minutes", "1 hour 5 minutes", "2h 5m" or "1d 6h 120m"; and that, if the field is set to a certain value (e.g. 75) programmatically, the field should show the correct string (1 hour 15 minutes)

So I have written a parser and a formatter function (see below), but how can I get my combobox to use these?

I already tried overriding the rawToValue/valueToRaw function pair, similar to what I found in the datefield code:

rawToValue:function(rawValue) {
    console.log('rawToValue');
    console.log(rawValue);
    return this.parse(rawValue) || rawValue || null;
},
valueToRaw:function(value) {
    console.log('valueToRaw');
    console.log(value);
    return this.format(value);
},

but they are not called, I don't get any console log output.

These are the parser/formatter functions:

Ext.define('AlarmTimeField',{
    extend:'Ext.form.field.ComboBox',
    format:function(minutes) {
        var a = [];
        Ext.each(this.units, function(unit) {
            if(minutes >= unit.minutes) {
                var unitCount = Math.floor(minutes/unit.minutes);
                console.log(unitCount);
                minutes = minutes-unitCount*unit.minutes;
                a.push("" + unitCount + " " + (unitCount==1?unit.singular:unit.plural));
            }
        });
        return a.join(' ');
    },
    parse:function(input) {
        if(!input) return 0;
        var me=this,
            inputSplit = input.split(' '),
            value = 0,
            lastNum = 0;
        Ext.each(inputSplit,function(input) {
            if(!input) return;
            else if(Ext.isNumeric(input)) lastNum = input;
            else if(Ext.isNumeric(input[0])) {
                var inputUnit = input.slice(-1),
                    inputValue = input.slice(0,-1);
                Ext.each(me.units,function(unit) {
                    if(inputUnit==unit.abbr) {
                        value+=unit.minutes*inputValue;
                    }
                });
            }
            else {
                Ext.each(me.units,function(unit) {
                    if(input==unit.singular || input==unit.plural || input==unit.abbr) {
                        value+=unit.minutes*lastNum;
                    }
                });
            }
        });
        return value;
    },
    units:[{
        minutes:10080,
        abbr:'w',
        singular:'week',
        plural:'weeks'
    },{
        minutes:1440,
        abbr:'d',
        singular:'day',
        plural:'days'
    },{
        minutes:60,
        abbr:'h',
        singular:'hour',
        plural:'hours'
    },{
        minutes:1,
        abbr:'m',
        singular:'minute',
        plural:'minutes'
    }]
});
like image 847
Alexander Avatar asked Nov 08 '22 01:11

Alexander


1 Answers

The main idea is that Ext.form.field.ComboBox value is actually instance of Ext.data.Model, so your value and displayed value is just model attribute values and each time you change value / displayed value you have to update binded model instance (its my vision, correct me if I'm wrong).

I think that Ext.form.field.ComboBox.validator is nice place to parse manually inputted values (and you can instantly display error message if inputted value is incorrect), so you can override it like this:

                validator: function (value) {
                    // TODO: Add regexp value format validator

                    var minutes = me.parse(value);
                    // Add check for zero / empty values if needed
                    if (minutes === 0)
                    // Add meaningful error message
                        return 'Incorrect input';
                    else {
                        me.setValue(Ext.create('Ext.data.Model', {
                            value: minutes,
                            text: value
                        }));
                        return true;
                    }
                }

Its quite raw example, but I think that idea is clear.

To format values setted programatically via setValue() method you can override this method, like this:

        setValue: function (value) {
            // TODO: Add array of values support
            if (Ext.isNumber(value))
                value = Ext.create('Ext.data.Model', {
                    value: value,
                    text: this.format(value)
                });

            this.callParent([value]);
        }

Check this fork of your fiddle. I hope that I helped a little.

like image 129
Sergey Novikov Avatar answered Nov 14 '22 23:11

Sergey Novikov