Javascript 如何保持相互依赖的knockout.js同步可见

Javascript 如何保持相互依赖的knockout.js同步可见,javascript,knockout.js,Javascript,Knockout.js,我有3个相互关联的敲除观测值。是的 零售价、售价、折扣 当用户更改一个值时,其他观察值将更新。 比如说, 如果用户将售价输入为1000,则零售价设置为1000,折扣设置为0 按照上面的示例,现在如果用户将零售价格编辑为2000,折扣将更新为50 现在,如果我没有afterkeydown事件,所有这些工作都很好。因此,当用户输入任何值并移动到下一个框时,就可以了 问题是,当我保持afterkeydown时,只要我开始键入,就会根据第一个字符计算其他值。现在,当我输入第二个整数时,值被弄乱了 比

我有3个相互关联的敲除观测值。是的

零售价、售价、折扣

当用户更改一个值时,其他观察值将更新。 比如说,

  • 如果用户将售价输入为1000,则零售价设置为1000,折扣设置为0
  • 按照上面的示例,现在如果用户将零售价格编辑为2000,折扣将更新为50
现在,如果我没有afterkeydown事件,所有这些工作都很好。因此,当用户输入任何值并移动到下一个框时,就可以了

问题是,当我保持afterkeydown时,只要我开始键入,就会根据第一个字符计算其他值。现在,当我输入第二个整数时,值被弄乱了

比如说,

售价=10,零售价=1,折扣=900

我想不出解决这个问题的办法。有什么建议吗

问候,

更新

抱歉说不清楚。用户可以更新这三个字段中的任何一个。我已经为此制作了一把小提琴。目前,我无法控制用值填充字段的顺序。它们可以按任何顺序填写

  • 从零售开始:销售设置为零售,折扣=0
  • 从销售开始:零售设置为销售,折扣=0
  • 从折扣开始。下一步可以添加零售或销售
用户可以按任意顺序输入值

Html:


零售价:
售价:
折扣:
Javascript:

函数hasOwnProperty(对象、属性){
var proto=obj.uu proto_u124; | obj.constructor.prototype;
返回(对象中的道具)&(!(对象中的道具)| |对象[道具]!==对象[道具];
}
函数轮号(值、精度、flt){
var精度=精度| | 0,
负=值<0,
功率=数学功率(10,精度),
数值=数学四舍五入(数值*幂),
整数=字符串((neg?Math.ceil:Math.floor)(值/幂)),
分数=字符串((负?-value:value)%power),
padding=新数组(Math.max(precision-fraction.length,0)+1);
如果(flt==真){
积分=浮点(积分);
}
返回精度?整数+'。+填充+分数:整数;
}
var util={};
util.format=函数(值,前缀){
var pr=前缀| |'';
toks=轮号(值,2)。替换('-','')。拆分('.');
var display=pr+$.map(toks[0]。拆分(“”).reverse(),函数(elm,i){
返回[(i%3===0&&i>0?',':'',elm];
}).reverse().join(“”)+.+toks[1];
返回值<0?'-'+显示:显示;
};
ko.subscribable.fn.formatted=函数(选项){
var目标=此;
var_options=(options==未定义)?{}:选项;
var _prefix=hasOwnProperty(_options,'prefix')?_options.prefix:“”;
var _precision=hasOwnProperty(_options,'precision')?_options.precision:2;
var_type=hasOwnProperty(_options,'type')?_options.type:2;
变量格式=函数(值){
开关(U型){
案例1:
return util.format(roundNumber(值,_精度),_前缀);//带符号的货币
案例2:
返回roundNumber(值,_精度);//reg float
违约:
抛出新错误(“非法类型”);
}
};
var聚焦=可观察(假);
var writeTarget=函数(值){
var=价值;
如果(isNaN(值)){
stripped=字符串(值)。替换(/[^0-9.-]/g');
}
//目标(浮动(剥离));
值=浮动(剥离);
聚焦()?目标(!isFinite(value)?0:value):目标(!isFinite(value)?0:roundNumber(value,_precision));//写入底层存储
};
var结果=ko.computed({
读:函数(){
返回目标();
},
write:writeTarget
});
result.formatted=ko.computed({
读:函数(){
if(focused()){
return(isNaN(target())?“”:target());//写入底层存储
}
返回格式(target());
},
write:writeTarget
});
result.isNegative=ko.computed(函数(){
返回目标()<0;
});
result.isFocused=ko.computed({
读:函数(){
返回聚焦();
},
写入:函数(值){
专注(价值);
}
});
返回结果;
};
var ViewModel=函数(){
var self=这个;
self.retail_price=ko.可观察(0).扩展({
油门:100
}).格式化({
类型:1
});
自售价格=ko.可观察(0).扩展({
油门:100
}).格式化({
类型:1
});
自贴现=可观测的ko(0);
//只要零售价格发生变化,就改变珠宝的销售价格
self.retail\u price.subscribe(函数(){
if(parseFloat(self.salling_price())!==0){
self.updateDiscovery();
返回;
}
if(self.retail\u price.isFocused()){
self.changeSellingPrice();
}
});
//只要折扣改变,就改变售价
self.discount.subscribe(函数(){
self.changeSellingPrice();
});
自售价格订阅(功能五){
if(self.self\u price.isFocused()){
if(parseFloat(self.retail_price())!==0){
self.updateDiscovery();
返回;
}
var-retPrice=(v*100)/(100-self.discount());
自零售价(伊斯南(retPrice)?0:整数(retPrice,2));
}
});
self.updateDiscovery=函数(){
var retPr=parseFloat(self.retail_price());
弗吉尼亚州
<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>
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());
var discount = ko.computed(function() {
  var discount;

  // your calculation goes here
  // discount = retail_price() - selling_price() …

  return discount > 0 ? discount : 0;
});
<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>
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());