我有3个相互关联的淘汰观察者。他们是
retail_price,selling_price,discount
当用户更改一个值时,将更新其他其他可观察对象。 例如,
如果我没有afterkeydown事件,现在这一切都很好。因此,当用户输入任何值并移动到下一个框时,就可以了。
问题在于,当我继续追踪时,一旦我开始输入,基于第一个字符,就会计算其他值。现在,当我输入第二个整数时,这些值会混乱。
例如,
selling_price = 10,retail_price = 1,discount = -900
我无法找到解决方法。有什么建议吗?
问候,
更新
很抱歉不清楚。用户可以更新三个字段中的任何一个。我为此创造了一个小提琴。目前,我无法控制填充值的字段的顺序。它们可以按任何顺序填写。
用户可以按任何顺序输入值。
HTML:
<code>
<div>retail_price:
<input type="text" data-bind="value: retail_price.formatted, hasfocus: retail_price.isFocused, valueUpdate: 'afterkeydown'" />
</div>
<div>selling_price:
<input type="text" data-bind="value: selling_price.formatted, hasfocus: selling_price.isFocused, valueUpdate: 'afterkeydown'" />
</div>
<div>discount:
<input type="text" data-bind="value: discount, hasfocus: discount.isFocused, valueUpdate: 'afterkeydown'" />
</div>
</code>
使用Javascript:
function hasOwnProperty(obj, prop) {
var proto = obj.__proto__ || obj.constructor.prototype;
return (prop in obj) && (!(prop in proto) || proto[prop] !== obj[prop]);
}
function roundNumber(value, precision, flt) {
var precision = precision || 0,
neg = value < 0,
power = Math.pow(10, precision),
value = Math.round(value * power),
integral = String((neg ? Math.ceil : Math.floor)(value / power)),
fraction = String((neg ? -value : value) % power),
padding = new Array(Math.max(precision - fraction.length, 0) + 1).join('0');
if (flt === true) {
integral = parseFloat(integral);
}
return precision ? integral + '.' + padding + fraction : integral;
}
var util = {};
util.format = function (value, prefix) {
var pr = prefix || '';
toks = roundNumber(value, 2).replace('-', '').split('.');
var display = pr + $.map(toks[0].split('').reverse(), function (elm, i) {
return [(i % 3 === 0 && i > 0 ? ',' : ''), elm];
}).reverse().join('') + '.' + toks[1];
return value < 0 ? '-' + display : display;
};
ko.subscribable.fn.formatted = function (options) {
var target = this;
var _options = (options === undefined) ? {} : options;
var _prefix = hasOwnProperty(_options, 'prefix') ? _options.prefix : '';
var _precision = hasOwnProperty(_options, 'precision') ? _options.precision : 2;
var _type = hasOwnProperty(_options, 'type') ? _options.type : 2;
var format = function (value) {
switch (_type) {
case 1:
return util.format(roundNumber(value, _precision), _prefix); //currency w/ symbol
case 2:
return roundNumber(value, _precision); //reg float
default:
throw new Error('illegal type');
}
};
var focused = ko.observable(false);
var writeTarget = function (value) {
var stripped = value;
if (isNaN(value)) {
stripped = String(value).replace(/[^0-9.-]/g, '');
}
//target(parseFloat(stripped));
value = parseFloat(stripped);
focused() ? target(!isFinite(value) ? 0 : value) : target(!isFinite(value) ? 0 : roundNumber(value, _precision)); // Write to underlying storage
};
var result = ko.computed({
read: function () {
return target();
},
write: writeTarget
});
result.formatted = ko.computed({
read: function () {
if (focused()) {
return (isNaN(target()) ? '' : target()); // Write to underlying storage
}
return format(target());
},
write: writeTarget
});
result.isNegative = ko.computed(function () {
return target() < 0;
});
result.isFocused = ko.computed({
read: function () {
return focused();
},
write: function (value) {
focused(value);
}
});
return result;
};
var ViewModel = function () {
var self = this;
self.retail_price = ko.observable(0).extend({
throttle: 100
}).formatted({
type: 1
});
self.selling_price = ko.observable(0).extend({
throttle: 100
}).formatted({
type: 1
});
self.discount = ko.observable(0);
// Whenever the retail price changes, change the selling price for jewelry
self.retail_price.subscribe(function () {
if (parseFloat(self.selling_price()) !== 0) {
self.updateDiscount();
return;
}
if (self.retail_price.isFocused()) {
self.changeSellingPrice();
}
});
// Whenever the discount changes, change the selling price
self.discount.subscribe(function () {
self.changeSellingPrice();
});
self.selling_price.subscribe(function (v) {
if (self.selling_price.isFocused()) {
if (parseFloat(self.retail_price()) !== 0) {
self.updateDiscount();
return;
}
var retPrice = (v * 100) / (100 - self.discount());
self.retail_price(isNaN(retPrice) ? 0 : roundNumber(retPrice, 2));
}
});
self.updateDiscount = function () {
var retPr = parseFloat(self.retail_price());
var askPr = parseFloat(self.selling_price());
var discount = 100 * (retPr - askPr) / retPr;
self.discount(!isFinite(discount) ? 0 : roundNumber(discount, 2));
};
self.changeSellingPrice = function () {
var sellingPrice = self.retail_price() - (self.retail_price() * self.discount()) / 100;
self.selling_price(isNaN(sellingPrice) ? 0 : roundNumber(sellingPrice, 2));
};
};
ko.applyBindings(new ViewModel());
function hasOwnProperty(obj, prop) {
var proto = obj.__proto__ || obj.constructor.prototype;
return (prop in obj) && (!(prop in proto) || proto[prop] !== obj[prop]);
}
function roundNumber(value, precision, flt) {
var precision = precision || 0,
neg = value < 0,
power = Math.pow(10, precision),
value = Math.round(value * power),
integral = String((neg ? Math.ceil : Math.floor)(value / power)),
fraction = String((neg ? -value : value) % power),
padding = new Array(Math.max(precision - fraction.length, 0) + 1).join('0');
if (flt === true) {
integral = parseFloat(integral);
}
return precision ? integral + '.' + padding + fraction : integral;
}
var util = {};
util.format = function (value, prefix) {
var pr = prefix || '';
toks = roundNumber(value, 2).replace('-', '').split('.');
var display = pr + $.map(toks[0].split('').reverse(), function (elm, i) {
return [(i % 3 === 0 && i > 0 ? ',' : ''), elm];
}).reverse().join('') + '.' + toks[1];
return value < 0 ? '-' + display : display;
};
ko.subscribable.fn.formatted = function (options) {
var target = this;
var _options = (options === undefined) ? {} : options;
var _prefix = hasOwnProperty(_options, 'prefix') ? _options.prefix : '';
var _precision = hasOwnProperty(_options, 'precision') ? _options.precision : 2;
var _type = hasOwnProperty(_options, 'type') ? _options.type : 2;
var format = function (value) {
switch (_type) {
case 1:
return util.format(roundNumber(value, _precision), _prefix); //currency w/ symbol
case 2:
return roundNumber(value, _precision); //reg float
default:
throw new Error('illegal type');
}
};
var focused = ko.observable(false);
var writeTarget = function (value) {
var stripped = value;
if (isNaN(value)) {
stripped = String(value).replace(/[^0-9.-]/g, '');
}
//target(parseFloat(stripped));
value = parseFloat(stripped);
focused() ? target(!isFinite(value) ? 0 : value) : target(!isFinite(value) ? 0 : roundNumber(value, _precision)); // Write to underlying storage
};
var result = ko.computed({
read: function () {
return target();
},
write: writeTarget
});
result.formatted = ko.computed({
read: function () {
if (focused()) {
return (isNaN(target()) ? '' : target()); // Write to underlying storage
}
return format(target());
},
write: writeTarget
});
result.isNegative = ko.computed(function () {
return target() < 0;
});
result.isFocused = ko.computed({
read: function () {
return focused();
},
write: function (value) {
focused(value);
}
});
return result;
};
var ViewModel = function () {
var self = this;
self.retail_price = ko.observable(0).extend({
throttle: 100
}).formatted({
type: 1
});
self.selling_price = ko.observable(0).extend({
throttle: 100
}).formatted({
type: 1
});
self.discount = ko.observable(0);
// Whenever the retail price changes, change the selling price for jewelry
self.retail_price.subscribe(function () {
if (parseFloat(self.selling_price()) !== 0) {
self.updateDiscount();
return;
}
if (self.retail_price.isFocused()) {
self.changeSellingPrice();
}
});
// Whenever the discount changes, change the selling price
self.discount.subscribe(function () {
self.changeSellingPrice();
});
self.selling_price.subscribe(function (v) {
if (self.selling_price.isFocused()) {
if (parseFloat(self.retail_price()) !== 0) {
self.updateDiscount();
return;
}
var retPrice = (v * 100) / (100 - self.discount());
self.retail_price(isNaN(retPrice) ? 0 : roundNumber(retPrice, 2));
}
});
self.updateDiscount = function () {
var retPr = parseFloat(self.retail_price());
var askPr = parseFloat(self.selling_price());
var discount = 100 * (retPr - askPr) / retPr;
self.discount(!isFinite(discount) ? 0 : roundNumber(discount, 2));
};
self.changeSellingPrice = function () {
var sellingPrice = self.retail_price() - (self.retail_price() * self.discount()) / 100;
self.selling_price(isNaN(sellingPrice) ? 0 : roundNumber(sellingPrice, 2));
};
};
ko.applyBindings(new ViewModel());
答案 0 :(得分:0)
您可以对计算的可观察量进行折扣并包含一些健全性检查:
var discount = ko.computed(function() {
var discount;
// your calculation goes here
// discount = retail_price() - selling_price() …
return discount > 0 ? discount : 0;
});
答案 1 :(得分:0)
我添加了另一个observable来跟踪用户是否输入了特定值。例如。如果用户输入了零售价值,则可观察到。
不确定这是否正确,但似乎有效。
答案 2 :(得分:0)
这个值没有更新,因为你检查过该值应该大于0(零)然后才会更新该值。现在你正在使用afterkeydown绑定,所以在按下一个数字后,它将值更新为最初为0(零),然后它因为值为非零而停止工作。
我假设在这种情况下你应该删除valueupdate绑定并删除isfocused的检查。我做了一些改变。如果您仍有任何疑虑,请仔细阅读并告知我们:
HTML:
<div>retail_price:
<input type="text" data-bind="value: retail_price.formatted, hasfocus: retail_price.isFocused" />
</div>
<div>selling_price:
<input type="text" data-bind="value: selling_price.formatted, hasfocus: selling_price.isFocused" />
</div>
<div>discount:
<input type="text" data-bind="value: discount, hasfocus: discount.isFocused" />
</div>
使用Javascript:
function hasOwnProperty(obj, prop) {
var proto = obj.__proto__ || obj.constructor.prototype;
return (prop in obj) && (!(prop in proto) || proto[prop] !== obj[prop]);
}
function roundNumber(value, precision, flt) {
var precision = precision || 0,
neg = value < 0,
power = Math.pow(10, precision),
value = Math.round(value * power),
integral = String((neg ? Math.ceil : Math.floor)(value / power)),
fraction = String((neg ? -value : value) % power),
padding = new Array(Math.max(precision - fraction.length, 0) + 1).join('0');
if (flt === true) {
integral = parseFloat(integral);
}
return precision ? integral + '.' + padding + fraction : integral;
}
var util = {};
util.format = function (value, prefix) {
var pr = prefix || '';
toks = roundNumber(value, 2).replace('-', '').split('.');
var display = pr + $.map(toks[0].split('').reverse(), function (elm, i) {
return [(i % 3 === 0 && i > 0 ? ',' : ''), elm];
}).reverse().join('') + '.' + toks[1];
return value < 0 ? '-' + display : display;
};
ko.subscribable.fn.formatted = function (options) {
var target = this;
var _options = (options === undefined) ? {} : options;
var _prefix = hasOwnProperty(_options, 'prefix') ? _options.prefix : '';
var _precision = hasOwnProperty(_options, 'precision') ? _options.precision : 2;
var _type = hasOwnProperty(_options, 'type') ? _options.type : 2;
var format = function (value) {
switch (_type) {
case 1:
return util.format(roundNumber(value, _precision), _prefix); //currency w/ symbol
case 2:
return roundNumber(value, _precision); //reg float
default:
throw new Error('illegal type');
}
};
var focused = ko.observable(false);
var writeTarget = function (value) {
var stripped = value;
if (isNaN(value)) {
stripped = String(value).replace(/[^0-9.-]/g, '');
}
//target(parseFloat(stripped));
value = parseFloat(stripped);
focused() ? target(!isFinite(value) ? 0 : value) : target(!isFinite(value) ? 0 : roundNumber(value, _precision)); // Write to underlying storage
};
var result = ko.computed({
read: function () {
return target();
},
write: writeTarget
});
result.formatted = ko.computed({
read: function () {
if (focused()) {
return (isNaN(target()) ? '' : target()); // Write to underlying storage
}
return format(target());
},
write: writeTarget
});
result.isNegative = ko.computed(function () {
return target() < 0;
});
result.isFocused = ko.computed({
read: function () {
return focused();
},
write: function (value) {
focused(value);
}
});
return result;
};
var ViewModel = function () {
var self = this;
self.retail_price = ko.observable(0).extend({
throttle: 100
}).formatted({
type: 1
});
self.selling_price = ko.observable(0).extend({
throttle: 100
}).formatted({
type: 1
});
self.discount = ko.observable(0);
// Whenever the retail price changes, change the selling price for jewelry
self.retail_price.subscribe(function () {
if (parseFloat(self.selling_price()) !== 0) {
self.updateDiscount();
return;
}
if (self.retail_price.isFocused()) {
self.changeSellingPrice();
}
});
// Whenever the discount changes, change the selling price
self.discount.subscribe(function () {
self.changeSellingPrice();
});
self.selling_price.subscribe(function (v) {
var retPrice = (v * 100) / (100 - self.discount());
self.retail_price(isNaN(retPrice) ? 0 : roundNumber(retPrice, 2));
if (parseFloat(self.retail_price()) !== 0) {
self.updateDiscount();
}
});
self.updateDiscount = function () {
var retPr = parseFloat(self.retail_price());
var askPr = parseFloat(self.selling_price());
var discount = 100 * (retPr - askPr) / retPr;
self.discount(!isFinite(discount) ? 0 : roundNumber(discount, 2));
};
self.changeSellingPrice = function () {
var sellingPrice = self.retail_price() - (self.retail_price() * self.discount()) / 100;
self.selling_price(isNaN(sellingPrice) ? 0 : roundNumber(sellingPrice, 2));
};
};
ko.applyBindings(new ViewModel());