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>
请注意,这是一个小片段,并且必须在我绑定数字的每个位置添加 .toFixed(1) 会导致比原来更混乱的标记显示在这里。
对于除数字之外的所有内容,覆盖 toString 是我控制输出内容的有效方法。关于如何告诉淘汰一次的方法有什么建议吗?以某种集中方式为我的页面使用什么函数在将数字添加到输出之前将其转换为字符串?
就此而言,拥有一种通用方法来告诉淘汰赛如何格式化任何类型的值似乎会很有用。重写 Date.prototype.toString 可以工作,但感觉有点笨重,因为它可能会影响 .toString 除了淘汰之外的其他用途。
I have a viewModel with a bunch of numbers with lots of decimal places. If my bindings look like this:
<tr>
<td data-bind="text: Date"></td>
<td data-bind="text: ActualWeight"></td>
<td data-bind="text: TrendWeight"></td>
</tr>
Then, of course, the output has all the decimal places and is very unreadable. Changing the bindings to look like this solves the problem, but is very verbose and "noisy":
<tr>
<td data-bind="text: Date"></td>
<td data-bind="text: ActualWeight().toFixed(1)"></td>
<td data-bind="text: TrendWeight().toFixed(1)"></td>
</tr>
Note, this is one small snippet and having to add .toFixed(1) every place I bind a number leads to much more messy markup than what is shown here.
For everything except numbers, overriding the toString has been an effective way for me to control what the output looks like. Any suggestions on a way to tell knockout once, in some central way for my page what function to use to convert numbers into strings before they are added to the output?
For that matter, having a general purpose way to tell knockout how to format any type of value seems like it would be useful. Overriding Date.prototype.toString works but feels a little heavy handed since it may impact other uses of .toString besides just knockout's.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
发布评论
评论(7)
由于knockout现在支持扩展程序,我将使用它们而不是自定义绑定。绑定看起来像这样:
<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
扩展程序。淘汰赛文档中提供了示例。
为了格式化货币和百分比,我创建了自定义绑定 numericformat.js,与 http:// 中找到的 numeric.min.js 一起使用adamwdraper.github.com/Numeral-js/
numericformat.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 %)"));
}
};
视图中的绑定示例。
<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>
以上面接受的答案为基础。我还分叉了 RP Niemeyers fiddle 来添加逗号格式。
因此,如果您有 10001.232,这将格式化为 10,001.232。如果您正在处理价格,这一点非常重要。再说一次,这只是建立在答案的基础上。
<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);
我使用 jQuery Globalize 插件进行格式化。
这是我的格式化处理程序版本,textFormatted
和 valueFormatted
分别是文本和值绑定的包装器。
用法是:
<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
参数需要。
对于将在可选的 locale
参数中使用的 culture
属性/绑定也是如此。
全球化参考。
绑定定义:
(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);
}
};
}());
让我们把事情变得更简单。更简单:
关键字是 unwrap,并注意 amount 后面的括号。 (这不必是您的淘汰对象的一部分。)
<script type="text/javascript">
function getDecimal(amount) {
var value = ko.unwrap(amount());
return parseFloat(value).toFixed(2);
}
...
<span data-bind="text: getDecimal(ItemAmount)">
或者,如果您这样做,则不必解开它(注意括号):
function getDecimal(amount) {
return parseFloat(amount).toFixed(2);
}
<span data-bind="text: getDecimal(ItemAmount())">
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
有几种方法可以处理这种情况。您可以选择通过绑定来解决它,也可以将其推入您的视图模型中。
如果您的视图模型是由映射插件创建的,并且您不想自定义其创建方式,那么您可以考虑使用自定义绑定,该绑定是文本绑定的包装器来处理格式。
类似于(http://jsfiddle.net/rniemeyer/RVL6q/):
这当然是可能的创建一个更通用的绑定(formattedText),它可以检查值并使用一些可覆盖的默认值对其进行格式化,或者允许您传递一些格式化选项(
{ type: "numeric", precision: 2}
)。对于您的场景,听起来第一个选项可能是一个不错的选择。但是,如果您想将其推送到视图模型中,那么您可以创建一个特殊的可观察量,它可以返回该值的格式化版本和原始版本。
它可能类似于 (http://jsfiddle.net/rniemeyer/fetBG/):
现在你根据您的需要,可能会绑定
myValue
和myValue.raw
。否则,您可以翻转它并默认返回原始值并公开一个格式化的 dependentObservable。当像这样的对象转换为 JSON 时,它将丢失任何“子可观察量”,因此如果您将此数据发送回服务器,可能需要考虑一下。您可以再次使其更加通用,并创建一个
formattedObservable
来接收有关如何格式化对象的一些信息。最后,1.3 beta 提供了一个
extenders
API。您可以执行类似于上面的操作,例如: (http://jsfiddle.net/rniemeyer/AsdES/ )然后,将其应用于可观察量,例如:
var myValue = ko.observable(1.223123).extend({numeric: 1});
您可以拥有扩展器也只是将一个
格式化
dependentObservable 添加到target
,而不是返回 dependentObservable 本身。There are a couple of ways that you can handle a situation like this one. You can either choose to address it through bindings or push it into your view model.
If your view model is created by the mapping plugin and you don't want to get into customizing the way that it is created, then you can consider using a custom binding that is a wrapper to the text binding to handle the formatting.
Something like (http://jsfiddle.net/rniemeyer/RVL6q/):
It certainly would be possible to create an even more generic binding (formattedText) that either inspected the value and formatted it using some overridable defaults or allowed you to pass in some formatting options (
{ type: "numeric", precision: 2 }
).For your scenario, it sounds like the first option might be a good choice. However, if you want to push it into your view model, then you could create a special observable that can return both a formatted and a raw version of the value.
It could be something like (http://jsfiddle.net/rniemeyer/fetBG/):
Now you could potentially bind against
myValue
andmyValue.raw
depending on your needs. Otherwise, you could flip it and return the raw value by default and expose aformatted
dependentObservable. When an object like this is converted to JSON, it will lose any of the "sub-observables", so if you are sending this data back to a server that might be a consideration.You could again make it more generic and create a
formattedObservable
that takes in some information about how to format the object.Finally, 1.3 beta offers an
extenders
API. You could do something similar to above like: (http://jsfiddle.net/rniemeyer/AsdES/)Then, apply it to an observable like:
var myValue = ko.observable(1.223123).extend({numeric: 1});
You could have the extender also just add a
formatted
dependentObservable totarget
instead of returning the dependentObservable itself.