Results 1 to 6 of 6

Thread: Numberfield decimalPrecision (currency) - Maybe a bug

  1. #1

    Question Numberfield decimalPrecision (currency) - Maybe a bug

    I've been wondering for a while why it is that if I set the decimalPrecision of a numberfield to 2, as for currency, it drops the trailing zero. So, I decided to have a further look into the problem. This forum post set me off http://www.extjs.com/forum/showthread.php?t=20040

    Looking at the source code for the numberfield I believe this is the offending code:
    Code:
    fixPrecision : function(value){
            var nan = isNaN(value);
            if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
               return nan ? '' : value;
            }
            return parseFloat(parseFloat(value).toFixed(this.decimalPrecision));
        },
    As you can see that the fixPrecision function is parsing a value as a float and setting it to a decimal precision. It then parses that value to a float again (I don't understand why) which then chops off any trailing zero.

    I did some basic testing of the parseFloat() function and this is what I found:
    Code:
    <script type="text/javascript">
    var value=1234.333643;
    var float=parseFloat(value).toFixed(2);
    alert(float);
    </script>
    alerts "1234.33" Rounding down.


    Code:
    <script type="text/javascript">
    var value=1234.306643;
    var float=parseFloat(value).toFixed(2);
    alert(float);
    </script>
    alerts "1234.31" Rounding up.


    Code:
    <script type="text/javascript">
    var value=1234.303643;
    var float=parseFloat(value).toFixed(2);
    alert(float);
    </script>
    alerts "1234.30" Rounding down and keeping the trailing zero.


    Now the problem:
    Code:
    <script type="text/javascript">
    var value=1234.303643;
    var float=parseFloat(parseFloat(value).toFixed(2));
    alert(float);
    </script>
    alerts "1234.3" Rounding down and dropping the trailing zero.

    My understanding is the parseFloat() does what is says and parses a value as a floating point value and that toFixed() rounds the floating point value to specified decimal precision. So why the extra parseFloat()?

    I thought I would ask here if anyone can see a flaw with my theory before I start reporting this as a bug.

    Looks like it could be an easy fix though.

  2. #2
    Sencha User Animal's Avatar
    Join Date
    Mar 2007
    Location
    Bédoin/Nottingham
    Posts
    30,892

    Default

    This is an FR for Field renderers.

    http://www.extjs.com/forum/showthrea...298#post329298

    It's not a bug. Setting an <input>'s value to 1.30 results in "1.3" appearing in it.

    a toString function won't append the arbitrary number of trailing zeroes that you are currently thinking of!

  3. #3

    Talking Currency Plugin

    Thanks for that Animal.

    I have actually created myself a plugin which does pretty much what I want. It's basically a numberfield with a few modifications. I am calling it into my application with my other plugins just after the base extjs files and generating currency fields when I need them.

    I can set it to use any character as the thousand separator and the decimal precision works with trailing zeros.

    I don't suppose it's the most elegant way of skinning the cat but it works for me.

    Code:
    Ext.namespace('Ext.ux');
     
    /**
    * Ext.ux.Currency Extension Class
    *
    * @author David Codona, aka Catapult
    * @version 1.0
    *
    * @class Ext.ux.Currency
    * @extends Ext.form.TextField
    * @constructor
    * @param {Object} config Configuration options
    */
    /**
    * Displays a currency value with a specified thousandSeparator and/or decimalSeparator.
    */
     
    Ext.ux.Currency = function(config) {
     
        // call parent constructor
        Ext.ux.Currency.superclass.constructor.call(this, config);
     
    }; // end of Ext.ux.Currency constructor
     
    Ext.extend(Ext.ux.Currency, Ext.form.TextField, {
     /**
     * @cfg {String} thousandSeparator Character to allow as the decimal separator (defaults to ',')
     */
     thousandSeparator : ',',
     /**
     * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
     */
     fieldClass: "x-form-field x-form-num-field",
     /**
     * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
     */
     allowDecimals : true,
     /**
     * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
     */
     decimalSeparator : ".",
     /**
     * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
     */
     decimalPrecision : 2,
     /**
     * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
     */
     allowNegative : true,
     /**
     * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
     */
     minValue : Number.NEGATIVE_INFINITY,
     /**
     * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
     */
     maxValue : Number.MAX_VALUE,
     /**
     * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
     */
     minText : "The minimum value for this field is {0}",
     /**
     * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
     */
     maxText : "The maximum value for this field is {0}",
     /**
     * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
     * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
     */
     nanText : "{0} is not a valid number",
     /**
     * @cfg {String} baseChars The base set of characters to evaluate as valid numbers (defaults to '0123456789').
     */
     baseChars : "0123456789",
     
     // private
     initEvents : function(){
      var allowed = this.baseChars + '';
      allowed += this.thousandSeparator + '';
      if (this.allowDecimals)
      {
       allowed += this.decimalSeparator;
      }
      if (this.allowNegative)
      {
       allowed += '-';
      }
      this.maskRe = new RegExp('[' + Ext.escapeRe(allowed) + ']');
      Ext.ux.Currency.superclass.initEvents.call(this);
     },
     // private
     validateValue : function(value){
      var num = this.parseValue(value);  
      if(!Ext.ux.Currency.superclass.validateValue.call(this, value))
      {
       return false;
      }
      if(value.length < 1)
      {
       // if it's blank and textfield didn't flag it then it's valid
       return true;
      }
      if(isNaN(num))
      {           
       this.markInvalid(String.format(this.nanText, value));
       return false;
      }
      if(num < this.minValue)
      {
       this.markInvalid(String.format(this.minText, this.minValue));
       return false;
      } 
      if(num > this.maxValue)
      {
       this.markInvalid(String.format(this.maxText, this.maxValue));
       return false;
      }        
      return true;
     },
     getValue : function(){
      var value = String(Ext.ux.Currency.superclass.getValue.call(this)).replace(this.thousandSeparator, "");
      return this.fixPrecision(this.parseValue(value));
     },
     setValue : function(v){
      v = Ext.isNumber(v) ? v : parseFloat(String(v).replace(this.decimalSeparator, "."));
      v = isNaN(v) ? '' : String(v).replace(".", this.decimalSeparator);  
      v = this.fixPrecision(v);
      v = this.currencyConvert(v);
      return Ext.ux.Currency.superclass.setValue.call(this, v);
     },
     /**
     * Replaces any existing {@link #minValue} with the new value.
     * @param {Number} value The minimum value
     */
     setMinValue : function(value){
      this.minValue = Ext.num(value, Number.NEGATIVE_INFINITY);
     },
     /**
     * Replaces any existing {@link #maxValue} with the new value.
     * @param {Number} value The maximum value
     */
     setMaxValue : function(value)
     {
      this.maxValue = Ext.num(value, Number.MAX_VALUE);
     },
     // private
     parseValue : function(value){
      value = String(value).replace(this.decimalSeparator, ".");
      value = String(value).replace(this.thousandSeparator, "");
      value = parseFloat(value);
      return isNaN(value) ? '' : value;
     },
     // private
     fixPrecision : function(value){
      var nan = isNaN(value);
      if(!this.allowDecimals || this.decimalPrecision <= -1 || nan || !value)
      {
       return nan ? '' : value;
      }
      return parseFloat(value).toFixed(this.decimalPrecision);
     },
     beforeBlur : function(){
      var v = this.getRawValue();
      if(this.validateValue(v))
      {
       v = this.parseValue(v);
       if(!Ext.isEmpty(v))
       {
        this.setValue(v);
       } 
      }
     },
     // private
     currencyConvert : function(v){
      var delimiter = this.thousandSeparator;
      var a = v.split('.',2)
      if(this.decimalPrecision>0)
      {
       var d = a[1];
      }
      else
      {
       var d = "";
      }
      var i = parseInt(a[0]);
      if(isNaN(i))
      {
       return '';
      }
      var minus = '';
      if(i < 0)
      {
       minus = '-';
      }
      i = Math.abs(i);
      var n = new String(i);
      var a = [];
      while(n.length > 3)
      {
       var nn = n.substr(n.length-3);
       a.unshift(nn);
       n = n.substr(0,n.length-3);
      }
      if(n.length > 0)
      {
       a.unshift(n);
      }
      n = a.join(delimiter);
      if(d.length < 1)
      {
       amount = n;
      }
      else
      {
       amount = n + '.' + d;
      }
      amount = minus + amount;
      return amount;
     }
    }); // end of extend
    // end of file

    Here's how I create the currency field:
    Code:
    new Ext.ux.Currency({
           thousandSeparator : ',',
           fieldLabel        : 'Currency Field',
           width             : 100,
           allowDecimals     : true,
           decimalPrecision  : 2,
           allowNegative     : true,
           //readOnly          : true,
           id                : 'histexcessplother',
           name              : 'histexcessplother',
           cls               : 'currency'  
    });

    I also have a css file which I put all my overrides in so I added:
    Code:
    /* Sets currency field text alignment to right */
    .currency {
        text-align: right;
    }
    I would be interested in any feed back as this is the first plugin I have created.

  4. #4
    Sencha User Animal's Avatar
    Join Date
    Mar 2007
    Location
    Bédoin/Nottingham
    Posts
    30,892

    Default

    That's not a plugin. It's a subclass of TextField.

    It probably does what you need, but what if there are other types of input which need decimal formatting?

    Maybe volumes, or some kind of scientific values. Maybe a field needs "?" putting after it or something like that?

    This is why a plugin is more flexible. You have kind of created an island with your functionality all in a subclass of its own. No other classes can use it.

    Check out http://www.extjs.com/blog/2009/11/11...t-with-ext-js/ for more about this.

    It's up to you. it's just something for you to consider in the future so as not to "Paint yourself into a corner" with specialized subclasses.

  5. #5

    Default

    You're quite right it is a subclass. I have plugin on the brain as that's how I'm using it within my app.

    I have a lot of currency fields within my app so this works fine for me. I will take on board what you have said for next time.

    Thanks

  6. #6
    Sencha User
    Join Date
    Mar 2008
    Posts
    5

    Exclamation

    Setting decimalSeparator to ',' doesn't work for me.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •