将ExtJS的NumberField扩展为CurrencyField

7

有一个非常好的答案,介绍了如何在NumberField中强制使用小数精度:如何在ExtJS NumberField中强制显示小数到特定精度?

但它只能解决一半的问题。我想再做一步,实现千位分隔符和美元符号(美国货币)。我试图将baseChars扩展为“1234567890 $,”,但没有成功。

是否有人已经解决了这个问题或者知道如何解决?


当您首次输入数字时,是否希望美元符号自动出现? - Johnathan Hebert
我认为用户应该能够将其放置,如果他没有这样做,则应自动添加 - 就像在示例中强制小数位一样。 - bensiu
4个回答

7
我昨天刚开发了你要找的扩展程序。我也需要这种功能,现在是完成它的时候了。
你可以在这个博客中阅读帖子并下载源代码:http://develtech.wordpress.com/2011/03/06/number-field-with-currency-symbol-thousand-separator-with-international-support 或者
访问ExtJS论坛中的这个帖子:http://www.sencha.com/forum/showthread.php?125937-Number-field-with-currency-symbol-thousand-separator-with-international-support&p=577701#post577701 希望对你有所帮助。

代码:

    /**
 * Copyright(c) 2011
 *
 * Licensed under the terms of the Open Source LGPL 3.0
 * http://www.gnu.org/licenses/lgpl.html
 * @author Greivin Britton, brittongr@gmail.com
 *     
 * @changes
 * No currency symbol by default    
 * No decimalPrecision in config
 * Supporting any character as thousand separator
 * Improved getFormattedValue
 * Removed unnecessary code to create format template, now using float.toFixed(this.decimalPrecission)    
 */

Ext.ux.NumericField = function(config){
    var defaultConfig = 
    {
        style: 'text-align:right;'
    };

    Ext.ux.NumericField.superclass.constructor.call(this, Ext.apply(defaultConfig, config));

    //Only if thousandSeparator doesn't exists is assigned when using decimalSeparator as the same as thousandSeparator
    if(this.useThousandSeparator && this.decimalSeparator == ',' && Ext.isEmpty(config.thousandSeparator))
        this.thousandSeparator = '.';
    else
        if(this.allowDecimals && this.thousandSeparator == '.' && Ext.isEmpty(config.decimalSeparator))
            this.decimalSeparator = ',';

    this.onFocus = this.onFocus.createSequence(this.onFocus);
};

Ext.extend(Ext.ux.NumericField, Ext.form.NumberField, 
{
    currencySymbol: null,
    useThousandSeparator: true,
    thousandSeparator: ',',
    alwaysDisplayDecimals: false,
    setValue: function(v){
       Ext.ux.NumericField.superclass.setValue.call(this, v);

       this.setRawValue(this.getFormattedValue(this.getValue()));
    },
    /**
     * No more using Ext.util.Format.number, Ext.util.Format.number in ExtJS versions
     * less thant 4.0 doesn't allow to use a different thousand separator than "," or "."
     * @param {Number} v
     */
    getFormattedValue: function(v){

        if (Ext.isEmpty(v) || !this.hasFormat()) 
            return v;
        else 
        {
            var neg = null;

            v = (neg = v < 0) ? v * -1 : v; 
            v = this.allowDecimals && this.alwaysDisplayDecimals ? v.toFixed(this.decimalPrecision) : v;

            if(this.useThousandSeparator)
            {
                if(this.useThousandSeparator && Ext.isEmpty(this.thousandSeparator))
                    throw ('NumberFormatException: invalid thousandSeparator, property must has a valid character.');

                if(this.thousandSeparator == this.decimalSeparator)
                    throw ('NumberFormatException: invalid thousandSeparator, thousand separator must be different from decimalSeparator.');

                var v = String(v);

                var ps = v.split('.');
                ps[1] = ps[1] ? ps[1] : null;

                var whole = ps[0];

                var r = /(\d+)(\d{3})/;

                var ts = this.thousandSeparator;

                while (r.test(whole)) 
                    whole = whole.replace(r, '$1' + ts + '$2');

                v = whole + (ps[1] ? this.decimalSeparator + ps[1] : '');
            }

            return String.format('{0}{1}{2}', (neg ? '-' : ''), (Ext.isEmpty(this.currencySymbol) ? '' : this.currencySymbol + ' '), v);
        }
    },
    /**
     * overrides parseValue to remove the format applied by this class
     */
    parseValue: function(v){
        //Replace the currency symbol and thousand separator
        return Ext.ux.NumericField.superclass.parseValue.call(this, this.removeFormat(v));
    },
    /**
     * Remove only the format added by this class to let the superclass validate with it's rules.
     * @param {Object} v
     */
    removeFormat: function(v){
        if (Ext.isEmpty(v) || !this.hasFormat()) 
            return v;
        else 
        {
            v = v.replace(this.currencySymbol + ' ', '');

            v = this.useThousandSeparator ? v.replace(new RegExp('[' + this.thousandSeparator + ']', 'g'), '') : v;
            //v = this.allowDecimals && this.decimalPrecision > 0 ? v.replace(this.decimalSeparator, '.') : v;

            return v;
        }
    },
    /**
     * Remove the format before validating the the value.
     * @param {Number} v
     */
    getErrors: function(v){
        return Ext.ux.NumericField.superclass.getErrors.call(this, this.removeFormat(v));
    },
    hasFormat: function()
    {
        return this.decimalSeparator != '.' || this.useThousandSeparator == true || !Ext.isEmpty(this.currencySymbol) || this.alwaysDisplayDecimals;    
    },
    /**
     * Display the numeric value with the fixed decimal precision and without the format using the setRawValue, don't need to do a setValue because we don't want a double
     * formatting and process of the value because beforeBlur perform a getRawValue and then a setValue.
     */
    onFocus: function(){
        this.setRawValue(this.removeFormat(this.getRawValue()));
    }
});
Ext.reg('numericfield', Ext.ux.NumericField);

仅提供外部链接作为答案是不被赞同的(这些链接可能会在未知时间消失)。我已经将您的解决方案复制粘贴在上面,如果您真的不想要它,可以将其删除。 - Brock Adams
非常感谢,我只是觉得代码太大了...下次我会采纳这个建议的。 - brittongr

3

如果我理解正确,您想要强制使用双精度并添加美元符号。我认为最好的方法是向您的字段添加渲染器:

{
    ...
    fieldLabel: 'amount',
    renderer: MyApp.dollarRenderer
}

MyApp.dollarRenderer = function(amount) {
    return "$ "+sprintf('%.02f',amount);
}

-> 100.5 becomes $ 100.50

渲染器本身需要一个sprintf函数(出于某种神秘的原因,Javascript仍然没有本地实现)。但是我使用了一个不错的函数:http://www.diveintojavascript.com/projects/javascript-sprintf
要添加千位分隔符,请使用以下“好看”的正则表达式更改渲染器:
MyApp.dollarRenderer = function(amount) {
    return "$ "+sprintf('%.02f',amount).replace(/\d{1,3}(?=(\d{3})+(?!\d))/g,'$&,');
}
MyApp.dollarRenderer(1298129827.4) -> "$ 1,298,129,827.40"

祝你好运


当我要求一辆车时,你正在试图重新发明轮子(渲染器) - 这里有一个用于美国货币的ExtJS渲染器(名称:USMoney) - 问题是任何渲染器都可以与displayField(例如TextField)很好地工作,但我需要它在NumberField上扩展字符基础,强制执行适当格式化,千位分隔符,并且也具有原始值... - bensiu
3
嗯,对于我的开发来说,如果调整一个轮子需要的工作量比创建一个新的轮子还要多的话,我会选择创建一个新的轮子。 - Rob Boerman
3
除此之外,这是我第二次回答你的问题,我试图通过给你一些有用的东西来帮助你,而不仅仅是一个链接或者其他很多人做的事情。似乎你认为Stackoverflow社区是一种服务台。偶尔表示感谢会让那些花时间尝试帮助你的人感到欣慰。对于我来说,我将不再在你的问题上花费时间。 - Rob Boerman
很抱歉让你失望,但是你的两个答案都不能令我满意(这只是我的主观看法),并没有让我更接近解决问题,我想告诉你原因——请不要个人化——也许是我的英语或者提问方式的问题。 - bensiu
@rob,你的回答对我很有用。有时候它对其他人也有用,而不仅仅是对提问者。 我建议使用Ext.util.Format.number而不是自定义实现sprintf。类似这样: '$' + Ext.util.Format.number(amount, '0.00'); - jgrocha
@jrocha 我完全同意。不要采用我上面发布的4岁儿童建议 :) 使用Ext.util.Format,它非常出色。 - Rob Boerman

2
/**
 * @author: Frédéric Thomas
 * Date: 22/03/12
 * Time: 16:37
 */
Ext.define('yourNS.form.field.Currency', {
    extend: 'Ext.form.field.Number',
    alias: ['widget.currencyField'],
    config: {
        thousandSeparator: ' ',
        currencyAtEnd: true,
        currencySign: '€'
    },

    listeners: {
        /**
         * When this component get the focus, change the Currency
         * representation to a Float one for edition.
         *
         * @param me
         * @param eOpts
         */
        focus: function (me, eOpts) {
            me.inputEl.dom.value = this.getValue();
        }
    },

    /**
     * Converts a Float value into a currency formated value ready to display .
     *
     * @param {Object} value
     * @return {Object} The converted value.
     */
    valueToCurrency: function (value) {
        var format = Ext.util.Format;
        format.currencyPrecision = this.decimalPrecision;
        format.thousandSeparator = this.thousandSeparator;
        format.currencySign = this.currencySign;
        format.currencyAtEnd = true;
        return format.currency(value);
    },

    /**
     * Converts a mixed-type value to a raw representation suitable for displaying in the field. This allows controlling
     * how value objects passed to {@link #setValue} are shown to the user, including localization.
     *
     * See {@link #rawToValue} for the opposite conversion.
     *
     * This implementation converts the raw value to a value formated as currency.
     *
     * @param {Object} value The mixed-type value to convert to the raw representation.
     * @return {Object} The converted raw value.
     */
    valueToRaw: function (value) {
        return this.valueToCurrency(value);
    },

    /**
     * Performs any necessary manipulation of a raw String value to prepare it for conversion and/or
     * {@link #validate validation}. Overrided to apply the {@link #parseValue}
     * to the raw value.
     *
     * @param {String} value The unprocessed string value
     * @return {String} The processed string value
     */
    processRawValue: function (value) {
        return this.parseValue(this.callParent(arguments));
    },

    /**
     * Overrided to remove thousand separator.
     *
     * @param value
     */
    parseValue: function (value) {
        value = String(value).replace(this.thousandSeparator, "");
        value = parseFloat(String(value).replace(this.decimalSeparator, '.'));
        return isNaN(value) ? null : value;
    }
});

没有任何单词?你是机器人吗? - Pierre Arlaud

0

它是扩展TextField的,因此我将失去NumberField具有的所有好功能,而我喜欢... - bensiu
由于NumberField处理非字母数字字符的方式,您可能需要在这方面做出一些让步。 - JamesHalsall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接