web-dev-qa-db-ja.com

KnockoutJSでの数値のフォーマット規則

私はたくさんの小数点以下の数字の束を持つviewModelを持っています。私のバインディングが次のように見える場合:

    <tr>
        <td data-bind="text: Date"></td>
        <td data-bind="text: ActualWeight"></td>
        <td data-bind="text: TrendWeight"></td>
    </tr>

そして、もちろん、出力にはすべての小数点以下の桁があり、非常に読みにくいです。このようにバインディングを変更することで問題は解決しますが、非常に冗長で「うるさい」です。

    <tr>
        <td data-bind="text: Date"></td>
        <td data-bind="text: ActualWeight().toFixed(1)"></td>
        <td data-bind="text: TrendWeight().toFixed(1)"></td>
    </tr>

これは1つの小さなスニペットであり、番号をバインドするすべての場所に.toFixed(1)を追加する必要があるため、ここに示すものよりもはるかに厄介なマークアップになります。

数字以外のすべてについて、toStringをオーバーライドすることは、出力がどのように見えるかを制御するための効果的な方法です。ノックアウトを一度だけ伝える方法に関する提案、私のページの中心的な方法で、出力に追加される前に数字を文字列に変換するためにどの関数を使用するのですか?

さらに言えば、あらゆる種類の値をどのようにフォーマットするかをノックアウトに伝える汎用的な方法があると便利だと思われます。 Date.prototype.toStringのオーバーライドは機能しますが、ノックアウトだけでなく.toStringの他の使用に影響を与える可能性があるため、少し手に負えない感じがします。

60
Erv Walter

このような状況に対処するには、いくつかの方法があります。バインディングを介してアドレスするか、ビューモデルにプッシュするかを選択できます。

ビューモデルがマッピングプラグインによって作成され、作成方法をカスタマイズしたくない場合は、テキストバインディングのラッパーであるカスタムバインディングを使用して、書式設定を処理することを検討できます。

http://jsfiddle.net/rniemeyer/RVL6q/ )のようなもの:

_ko.bindingHandlers.numericText = {
    update: function(element, valueAccessor, allBindingsAccessor) {
       var value = ko.utils.unwrapObservable(valueAccessor()),
           precision = ko.utils.unwrapObservable(allBindingsAccessor().precision) || ko.bindingHandlers.numericText.defaultPrecision,
           formattedValue = value.toFixed(precision);

        ko.bindingHandlers.text.update(element, function() { return formattedValue; });
    },
    defaultPrecision: 1  
};
_

確かに、より一般的なバインディング(formattedText)を作成して、値を検査し、オーバーライド可能なデフォルトを使用してフォーマットするか、いくつかのフォーマットオプション(_{ type: "numeric", precision: 2 }_)を渡すことができます。

あなたのシナリオでは、最初のオプションが良い選択のように思えます。ただし、ビューモデルにプッシュする場合は、値の書式付きバージョンと生バージョンの両方を返すことができる特別なオブザーバブルを作成できます。

http://jsfiddle.net/rniemeyer/fetBG/ )のようになります:

_function formattedNumericObservable(initialValue, precision) {
    var _raw = ko.observable(initialValue),
        precision = precision || formattedNumericObservable.defaultPrecision,        
        //the dependentObservable that we will return
        result = ko.dependentObservable({
            read: function() {
               return _raw().toFixed(precision); 
            },
            write: _raw
        });

        //expose raw value for binding
        result.raw = _raw;

        return result;   
}
_

これで、必要に応じてmyValueおよび_myValue.raw_に対して潜在的にバインドできます。それ以外の場合は、デフォルトでそれを反転して生の値を返し、formatted dependentObservableを公開できます。このようなオブジェクトがJSONに変換されると、「サブオブザーバブル」のいずれかが失われるため、このデータをサーバーに返送する場合は考慮事項になります。

再度より汎用的にし、オブジェクトのフォーマット方法に関する情報を取り込むformattedObservableを作成できます。

最後に、1.3ベータ版はextenders AP​​Iを提供します。上記のような何かをすることができます:( http://jsfiddle.net/rniemeyer/AsdES/

_ko.extenders.numeric = function(target, precision) {
    var result = ko.dependentObservable({
        read: function() {
           return target().toFixed(precision); 
        },
        write: target 
    });

    result.raw = target;
    return result;
};
_

次に、次のようなオブザーバブルに適用します:var myValue = ko.observable(1.223123).extend({numeric: 1});

また、extenderは、dependentObservable自体を返す代わりに、formatted dependentObservableをtargetに追加するだけでもかまいません。

94
RP Niemeyer

ノックアウトは extenders をサポートするようになったため、カスタムバインディングの代わりにそれらを使用します。バインディングは次のようになります。

<tr>
    <td data-bind="text: Date.extend({format : 'date'})"></td>
    <td data-bind="text: ActualWeight.extend({format : 'weight'})"></td>
    <td data-bind="text: TrendWeight.extend({format : 'weight'})"></td>
</tr>

この場合、formatエクステンダーを記述する必要があります。例は、ノックアウトのドキュメントに記載されています。

22
molnarg

通貨とパーセントをフォーマットするために、 http://adamwdraper.github.com/Numeral-js/ にあるnumeric.min.jsで使用するカスタムバインディングnumericformat.jsを作成しました

numbersformat.js(dateformat.jsおよびmoment.min.jsに触発された)

var formatNumber = function (element, valueAccessor, allBindingsAccessor, format) {
    // Provide a custom text value
    var value = valueAccessor(), allBindings = allBindingsAccessor();
    var numeralFormat = allBindingsAccessor.numeralFormat || format;
    var strNumber = ko.utils.unwrapObservable(value);
    if (strNumber) {
        return numeral(strNumber).format(numeralFormat);
    }
    return '';
};

ko.bindingHandlers.numeraltext = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        $(element).text(formatNumber(element, valueAccessor, allBindingsAccessor, "(0,0.00)"));  
    },
    update: function (element, valueAccessor, allBindingsAccessor) {
        $(element).text(formatNumber(element, valueAccessor, allBindingsAccessor, "(0,0.00)"));
    }
};

ko.bindingHandlers.numeralvalue = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        $(element).val(formatNumber(element, valueAccessor, allBindingsAccessor, "(0,0.00)"));

        //handle the field changing
        ko.utils.registerEventHandler(element, "change", function () {
            var observable = valueAccessor();
            observable($(element).val());
        });        
    },
    update: function (element, valueAccessor, allBindingsAccessor) {
        $(element).val(formatNumber(element, valueAccessor, allBindingsAccessor, "(0,0.00)"));
    }
};

ko.bindingHandlers.percenttext = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        $(element).text(formatNumber(element, valueAccessor, allBindingsAccessor, "(0.000 %)"));
    },
    update: function (element, valueAccessor, allBindingsAccessor) {
        $(element).text(formatNumber(element, valueAccessor, allBindingsAccessor, "(0.000 %)"));
    }
};

ko.bindingHandlers.percentvalue = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        $(element).val(formatNumber(element, valueAccessor, allBindingsAccessor, "(0.000 %)"));

        //handle the field changing
        ko.utils.registerEventHandler(element, "change", function () {
            var observable = valueAccessor();
            observable($(element).val());
        });
    },
    update: function (element, valueAccessor, allBindingsAccessor) {
        $(element).val(formatNumber(element, valueAccessor, allBindingsAccessor, "(0.000 %)"));
    }
};

Viewのバインディングの例。

        <td><label>Available Commitment Balance:</label> </td>
        <td>
            <!-- ko with: SelectedLoan -->
            <span data-bind="numeraltext: AvailableCommitmentAmount"></span>            
            <!-- /ko -->
        </td>
        <td><label> % Interest Rate:</label></td>
        <td>
            <!-- ko with: SelectedLoan -->
            <input  data-bind="percentvalue: InterestRatePercent" />
            <!-- /ko -->
        </td>
        <td><label> $ Amount To Transfer:</label></td>
        <td>
            <!-- ko with: SelectedLoan -->
            <input class="inputsmall" data-bind="numeralvalue: FundsHeldTotalAmount" />
            <!-- /ko -->
        </td>
10
user2161006

上記の受け入れられた答えに基づいて構築します。 RP Niemeyers fiddleを分岐させて、コンマ形式も追加しました。したがって、10001.232がある場合、これは10,001.232としてフォーマットされます。価格で作業している場合は非常に重要です。繰り返しますが、これは答えに基づいています。

JSFiddle

<div data-bind="numericText: myValue"></div>
<div data-bind="numericText: myValue, positions: 3"></div>
<div data-bind="numericText: myValue, positions: myPositions"></div>
<input data-bind="value: myPositions" />

<div>
    <br>
    just testing commas<br>
    <input type=text id="withComma" readonly/>
</div>
ko.bindingHandlers.numericText = {
    update: function(element, valueAccessor, allBindingsAccessor) {
       var value = ko.utils.unwrapObservable(valueAccessor());
       var positions= ko.utils.unwrapObservable(allBindingsAccessor().positions) || ko.bindingHandlers.numericText.defaultPositions;
       var formattedValue = value.toFixed(positions); 
       var finalFormatted = ko.bindingHandlers.numericText.withCommas(formattedValue);  

        ko.bindingHandlers.text.update(element, function() { return finalFormatted ; });
    },

    defaultPositions: 2,

    withCommas: function(original){
       original+= '';
     x = original.split('.');
    x1 = x[0];
    x2 = x.length > 1 ? '.' + x[1] : '';
    var rgx = /(\d+)(\d{3})/;
    while (rgx.test(x1)) {
        x1 = x1.replace(rgx, '$1' + ',' + '$2');
    }
    return x1 + x2;

    } 
};

var viewModel = {
    myValue: ko.observable(12673.554),
    myPositions: ko.observable(4)
};

ko.applyBindings(viewModel);

/*Just testing the function below, you don't need thsi....*/     



function addCommas(nStr)
{
    nStr += '';
    x = nStr.split('.');
    x1 = x[0];
    x2 = x.length > 1 ? '.' + x[1] : '';
    var rgx = /(\d+)(\d{3})/;
    while (rgx.test(x1)) {
        x1 = x1.replace(rgx, '$1' + ',' + '$2');
    }
    return x1 + x2;
}
var formatted = addCommas('1070781.493')
$('#withComma').val(formatted);
9
Stephen R.

JQuery Globalizeプラグインを使用してフォーマットに取り組みました。ここに私のバージョンのフォーマットハンドラがあります。textFormattedvalueFormattedは、それぞれテキストと値のバインディングのラッパーです。

使用法は次のとおりです。

_<span data-bind="textFormatted: Amount, pattern: 'n'" />
_

オプションで、カルチャも指定できます。しかし、この種のコントロールはHTMLに属すべきではないと思いますが、開発時またはデバッグ時に役立ちます。

_<input data-bind="valueFormatted: Amount, pattern: 'n', culture: 'et'" type="text" />
_

patternプロパティ/バインディングの値は、Globalize.format( value, format, [locale] )関数のformat paramが期待する適切な形式のいずれかでなければなりません。オプションのculture paramで使用されるlocaleプロパティ/バインディングについても同様です。 参照のグローバル化

バインディング定義:

_(function() {

    function getFormatedOrPlainResult(value, allBindingsAccessor) {
        var pattern = allBindingsAccessor.get('pattern');

        if (pattern == null || !/\S*/.test(pattern)) {
            return value;
        }
        var valueToFormat = pattern === 'd' ? new Date(value) : value;
        return Globalize.format(valueToFormat, pattern, allBindingsAccessor.get('culture'));
    };

    ko.bindingHandlers.textFormatted = {
        init: ko.bindingHandlers.text.init,
        update: function(element, valueAccessor, allBindingsAccessor) {
            var result = getFormatedOrPlainResult(ko.unwrap(valueAccessor()), allBindingsAccessor);
            ko.bindingHandlers.text.update(element, function() { return result; });
        }
    };

    ko.bindingHandlers.valueFormatted = {
        init: function(element, valueAccessor, allBindingsAccessor) {
            var result = getFormatedOrPlainResult(ko.unwrap(valueAccessor()), allBindingsAccessor);
            ko.bindingHandlers.value.init(element, function() { return result; }, allBindingsAccessor);
        },
        update: function(element, valueAccessor, allBindingsAccessor) {
            var result = getFormatedOrPlainResult(ko.unwrap(valueAccessor()), allBindingsAccessor);
            ko.bindingHandlers.value.update(element, function() { return result; }, allBindingsAccessor);
        }
    };
}());
_
2
LauriSaar

ローカライズされた数のテキストバインディングを表示するだけの場合、非常に簡単な方法はtoLocaleString()を使用することです

<tr>
  <td data-bind="text: ActualWeight().toLocaleString()"></td>
  <td data-bind="text: TrendWeight().toLocaleString()"></td>
</tr>

詳細については、 page をご覧ください。

0
Lars Aicher