observable and computed are not working properly Knockout - javascript

I have the following knockout code that implements the folowing role:
field1 + field2 -field3 = field4
$(function () {
ko.applyBindings(new AppViewModel());
});
function AppViewModel() {
this.original = ko.observable(0);
this.improvements = ko.observable(0);
this.depreciation = ko.observable(0);
this.total= ko.computed(function () {
var total= 0;
total= this.original() + this.improvements() - this.depreciation();
return total;
}, this);
}
But for some reason it's not working properly, this.original is always multiplied by 10.
for example:
1 + 1 - 1 = 10
Any idea what can causes this?
This is my HTML:
<div class="calc-form">
<label>Original Purchase Price</label>
<input type="text" id="original" data-bind="value: original" />
<label>+ Improvements</label>
<input type="text" id="improvements" data-bind="value: improvements" />
<label>- Depreciation</label>
<input type="text" id="depreciation" data-bind="value: depreciation" />
<input type="button" class="calcbutton" value="Calculate" />
<input type="button" class="calcbuttonreset" value="reset" />
<p>= Total</p>
<span data-bind="text: total"></span>
</div>

Remember that the value of input elements is always a string. "1" + "1" - "1" is "11" - "1" is 10 because when either operand is a string, + is string concatenation, not addition; but - is always subtraction, so it coerces its operands to numbers.
You need to parse them, via +x or parseFloat(x) or Number(x) or (if they're meant to be whole numbers) parseInt(x, 10), etc.:
total = parseFloat(this.original())
+ parseFloat(this.improvements())
- parseFloat(this.depreciation());
Example:
$(function() {
ko.applyBindings(new AppViewModel());
});
function AppViewModel() {
this.original = ko.observable(0);
this.improvements = ko.observable(0);
this.depreciation = ko.observable(0);
this.total = ko.computed(function() {
var total = 0;
total = parseFloat(this.original())
+ parseFloat(this.improvements())
- parseFloat(this.depreciation());
return total;
}, this);
}
<div class="calc-form">
<label>Original Purchase Price</label>
<input type="text" id="original" data-bind="value: original" />
<label>+ Improvements</label>
<input type="text" id="improvements" data-bind="value: improvements" />
<label>- Depreciation</label>
<input type="text" id="depreciation" data-bind="value: depreciation" />
<input type="button" class="calcbutton" value="Calculate" />
<input type="button" class="calcbuttonreset" value="reset" />
<p>= Total</p>
<span data-bind="text: total"></span>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
If you're going to be working with numeric inputs a lot, you might want to give yourself a specific binding for them:
// "numValue" binding handler (just an example)
ko.bindingHandlers.numValue = {
init: function(element, valueAccessor) {
function numValueHandler() {
valueAccessor()(parseFloat(this.value));
}
$(element).on("input change", numValueHandler)
.val(ko.unwrap(valueAccessor()));
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).off("input change", numValueHandler);
});
},
update: function(element, valueAccessor) {
element.value = ko.unwrap(valueAccessor());
}
};
Then:
<input type="text" id="original" data-bind="numValue: original" />
<!-- ---------------------------------------^^^^^^^^ -->
// "numValue" binding handler (just an example)
ko.bindingHandlers.numValue = {
init: function(element, valueAccessor) {
function numValueHandler() {
valueAccessor()(parseFloat(this.value));
}
$(element).on("input change", numValueHandler)
.val(ko.unwrap(valueAccessor()));
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).off("input change", numValueHandler);
});
},
update: function(element, valueAccessor) {
element.value = ko.unwrap(valueAccessor());
}
};
$(function() {
ko.applyBindings(new AppViewModel());
});
function AppViewModel() {
this.original = ko.observable(0);
this.improvements = ko.observable(0);
this.depreciation = ko.observable(0);
this.total = ko.computed(function() {
var total = 0;
total = this.original() + this.improvements() - this.depreciation();
return total;
}, this);
}
<div class="calc-form">
<label>Original Purchase Price</label>
<input type="text" id="original" data-bind="numValue: original" />
<label>+ Improvements</label>
<input type="text" id="improvements" data-bind="numValue: improvements" />
<label>- Depreciation</label>
<input type="text" id="depreciation" data-bind="numValue: depreciation" />
<input type="button" class="calcbutton" value="Calculate" />
<input type="button" class="calcbuttonreset" value="reset" />
<p>= Total</p>
<span data-bind="text: total"></span>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

Related

More efficient way of duplicating the code

Every time I click the + button I want the same input to display
The way I do it here works fine but seems like the worst way of doing it as just repeating the same code and changing the id's (also if I want for example 5 inputs I would have to repeat this code 5 times). What would be a better way of doing this?
<html>
<head>
<script language='JavaScript' type='text/javascript'>
function show3(){
document.getElementById('div2').style.display = 'block';
}
</script>
<style>
.hide {
display: none;
}
</style>
</head>
<body>
<div>
<input type="range" min="0" max="1500" value="0" class="slider2" id="one"/>
<p>Value(mm): <input type="text" id="two" size="10" class="special" /></p>
<button onclick="show3();" type="button">+</button>
</div>
<script>
var slider1 = document.getElementById("one");
var output2 = document.getElementById("two");
output2.value = slider1.value;
slider1.oninput = function() {
output2.value = this.value;
}
</script>
<div id="div2" class="hide">
<input type="range" min="0" max="1500" value="0" class="slider2" id="three"/>
<p>Value(mm): <input type="text" id="four" size="10" class="special" /></p>
<button onclick="show3();" type="button">+</button>
</div>
<script>
var slider2 = document.getElementById("three");
var output3 = document.getElementById("four");
output2.value = slider1.value;
slider2.oninput = function() {
output3.value = this.value;
}
</script>
</body>
</html>
<html>
<head>
<script language='JavaScript' type='text/javascript'>
function show3(){
document.getElementById('div2').style.display = 'block';
}
</script>
<style>
.hide {
display: none;
}
</style>
</head>
<body>
<div>
<input type="range" min="0" max="1500" value="0" class="slider2" id="one"/>
<p>Value(mm): <input type="text" id="two" size="10" class="special" /></p>
<button onclick="show3();" type="button">+</button>
</div>
<script>
var slider1 = document.getElementById("one");
var output2 = document.getElementById("two");
output2.value = slider1.value;
slider1.oninput = function() {
output2.value = this.value;
}
</script>
<div id="div2" class="hide">
<input type="range" min="0" max="1500" value="0" class="slider2" id="three"/>
<p>Value(mm): <input type="text" id="four" size="10" class="special" /></p>
<button onclick="show3();" type="button">+</button>
</div>
<script>
var slider2 = document.getElementById("three");
var output3 = document.getElementById("four");
output2.value = slider1.value;
slider2.oninput = function() {
output3.value = this.value;
}
</script>
</body>
</html>
This will work for all the sliders. But you need to keep in mind a couple of things :
This will work only for the sliders that are already rendered in the DOM (even if they are hidden) if you render new sliders to the DOM you will need to attach the event listener as I did it in the foreach loop.
The input id (e.g "one") needs to match the output data-range="one"
function show3(){
document.getElementById('div2').style.display = 'block';
}
var sliders = document.querySelectorAll(".slider"); // slider = common class name
sliders.forEach(( slider ) => {
slider.addEventListener('input', (e) => {
const sliderId = e.target.id;
const output = document.querySelector(`[data-range=${sliderId}]`);
output.value = e.target.value;
});
});
<html>
<head>
<style>
.hide {
display: none;
}
</style>
</head>
<body>
<div>
<input type="range" min="0" max="1500" value="0" class="slider" id="one"/>
<p>Value(mm): <input type="text" data-range="one" id="two" size="10" class="special" /></p>
<button onclick="show3();" type="button">+</button>
</div>
<div id="div2" class="hide">
<input type="range" min="0" max="1500" value="0" class="slider" id="two"/>
<p>Value(mm): <input type="text" data-range="two" id="four" size="10" class="special" /></p>
<button onclick="show3();" type="button">+</button>
</div>
</body>
</html>
Might be easier to include the code in the element and clone it (parentNode is the div) :
<div>
<input type="range" min="0" max="1500" value="0"
oninput="this.parentNode.getElementsByTagName('INPUT')[1].value = this.value"/>
<p>Value(mm): <input type="text" size="10" /></p>
<button type="button"
onclick="this.parentNode.parentNode.append(this.parentNode.cloneNode(true))">+</button>
</div>
I would recommend you to create some kind of class which let you create slider components dynamically.
Here's a quick example (not optimized):
var SliderComponent = (function(doc) {
var defaults = {
containerSelector: 'body',
value: 0,
min: 0,
max: 1500,
inputSize: 10,
inputClass: 'special',
sliderClass: 'slider',
buttonClass: 'button'
}, options;
function SliderComponent(options) {
options = Object.assign({}, defaults, options || {});
this.container = getContainer(options);
this.input = createInput(options);
this.slider = createSlider(options);
this.removeButton = createButton(options.buttonClass, '-');
this.addButton = createButton(options.buttonClass, '+');
this.element = render.apply(this);
this.events = [];
this.events.push(
addEventListener.call(this, 'click', this.removeButton, function() {
this.destroy();
}),
addEventListener.call(this, 'click', this.addButton, function() {
new SliderComponent(options);
}),
addEventListener.call(this, 'input', this.slider, function(event) {
this.input.value = event.target.value;
}),
addEventListener.call(this, 'input', this.input, function(event) {
this.slider.value = event.target.value;
})
)
}
SliderComponent.prototype.destroy = function() {
this.events.forEach(function(e) {
e();
});
this.element.remove();
}
function addEventListener(name, element, listener) {
listener = listener.bind(this);
element.addEventListener(name, listener);
return function() {
element.removeEventListener(name, listener);
};
}
function getContainer(options) {
var container = doc.querySelector(options.containerSelector);
if(!container) {
throw new Error('Container for selector %s not found', options.containerSelector);
}
return container;
}
function createInput(options) {
var input = doc.createElement('input');
input.setAttribute('type', 'number');
input.setAttribute('min', options.min);
input.setAttribute('max', options.max);
input.setAttribute('size', options.inputSize);
input.classList.add(options.inputClass);
input.value = options.value;
return input;
}
function createSlider(options) {
var input = doc.createElement('input');
input.setAttribute('type', 'range');
input.setAttribute('min', options.min);
input.setAttribute('max', options.max);
input.classList.add(options.sliderClass);
input.value = options.value;
return input;
}
function createButton(klass, text) {
var button = doc.createElement('button');
button.setAttribute('type', 'button');
button.classList.add(klass);
button.textContent = text;
return button;
}
function render() {
var element = doc.createElement('div');
element.appendChild(this.slider);
element.appendChild(this.input);
element.appendChild(this.removeButton);
element.appendChild(this.addButton);
return this.container.appendChild(element);
}
return SliderComponent;
})(document);
var sliders = new SliderComponent();

In this code, i want to add values by clicking the value buttons

I want to add values by clicking the value buttons by each and every by the following function
function sum() {
var txtFirstNumberValue = document.getElementById('txt1').value;
var txtSecondNumberValue = document.getElementById('txt2').value;
var txtThirdNumberValue = document.getElementById('txt3').value;
var txtFourthNumberValue = document.getElementById('txt4').value;
var result = parseInt(txtFirstNumberValue) + parseInt(txtSecondNumberValue);
var result = parseInt(txtFirstNumberValue) + parseInt(txtThirdNumberValue);
var result = parseInt(txtFirstNumberValue) + parseInt(txtFourthNumberValue);
if (!isNaN(result)) {
document.getElementById('txt1').value = result;
}
}
// Here I gave the other two events to add to the text field by the id names
<!-- here I gave the three Value buttons and on click function -->
<input type="text" id="txt1" value="10" />
<input type="button" id="txt2" value="10" onClick="sum();" />
<input type="button" id="txt3" value="20" onClick="sum();" />
<input type="button" id="txt4" value="30" onClick="sum();" />
You can get the value of the button by passing this.value to the onClick function where this represent the element itself.
function sum(value) {
document.getElementById('txt1').value = Number(document.getElementById('txt1').value) + Number(value);
}
<input type="text" id="txt1" value="10" />
<input type="button" id="txt2" value="10" onClick="sum(this.value);" />
<input type="button" id="txt3" value="20" onClick="sum(this.value);" />
<input type="button" id="txt4" value="30" onClick="sum(this.value);" />
This javascript will help you.
<script>
function sum(e) {
var result = parseInt(e.value) + parseInt(document.getElementById('txt1').value);
if (!isNaN(result)) {
document.getElementById('txt1').value = result;
}
}
</script>

KnockoutJS Remove dynamic bindings

I've managed to get a prototype working with the help of others to dynamically add new inputs and next to it that specific inputs settings. However I've been trying to get to grips how to remove what I've added dynamically. Any ideas?
HTML
<div class="input-row" data-bind="foreach: inputItems">
<div class="input-row-item">
<div>
<label data-bind="text: label"></label>
<input data-bind="attr:{ name: name, placeholder: placeholder, disabled: disabled() === 'true', value: value, type: type }">
</div>
<div>
<input type="text" class="nb-remove" data-bind="value: label" placeholder="input label">
<input type="text" value="text" class="nb-remove" data-bind="value: type" placeholder="input type">
<input type="text" class="nb-remove" data-bind="value: name" placeholder="input name">
<input type="text" class="nb-remove" data-bind="value: placeholder" placeholder="input placeholder">
<input type="text" class="nb-remove" data-bind="value: disabled" placeholder="input disabled">
<input type="text" class="nb-remove" data-bind="value: value" placeholder="input value">
</div>
<div>
<button data-bind="click: removeInput">Remove this</button>
</div>
</div>
</div>
THE JS
$(function(){
var InputItem = function InputItem(label, type, name, placeholder, disabled, value) {
this.label = ko.observable(label);
this.type = ko.observable(type);
this.name = ko.observable(name);
this.placeholder = ko.observable(placeholder);
this.disabled = ko.observable(disabled);
this.value = ko.observable(value);
}
var ViewModel = function ViewModel() {
var that = this;
this.inputItems = ko.observableArray([]);
this.addInput = function addInput() {
that.inputItems.push(new InputItem());
};
this.removeInput = function removeInput(){
//remove input here
}
}
ko.applyBindings(new ViewModel());
});
You should try something like this
View Model:
var ViewModel = function() {
var that = this;
that.inputItems = ko.observableArray([new InputItem()]);
that.addInput = function () {
that.inputItems.push(new InputItem());
};
that.removeInput = function (item){
that.inputItems.remove(item);
}
}
ko.applyBindings(new ViewModel());
Working fiddle here
Few Suggestions:
1) As you assigned var that=this you try to use that consistently across vm
2) You can create a function name simply like this var fun=function() else you can just do like this function fun(){//blah blah}

Tricky toggling in KnockoutJS

Have a drill down form field with fields progressively appearing. While I've figured out the logic for making them appear one-at-a-time down the page, including a simple toggle, the issue is resetting all observables to their original state when "No" is clicked (and with all fields clearing). Currently if "Yes" is clicked a second time (after completing the form, then deciding "No") all the fields reappear at once instead of progressively. http://jsfiddle.net/hotdiggity/JJ6f6/
Knockout model:
var ViewModel = function () {
var self = this;
self.showField1 = ko.observable(true);
self.showField2 = ko.observable(false);
self.showField3 = ko.observable(false);
self.showField4 = ko.observable(false);
self.yesOrNo = ko.observable("");
self.hasValue = ko.observable("");
self.toggleCalc = ko.observable("");
self.showField2 = ko.computed(function () {
return self.yesOrNo() == 'yes';
});
self.showField3 = ko.computed(function () {
self.showField4(false);
return ( !! self.hasValue());
});
self.toggleCalc = function () {
self.showField4(!self.showField4());
};
};
ko.bindingHandlers.fadeVisible = {
init: function (element, valueAccessor) {
var value = valueAccessor();
$(element).toggle(ko.utils.unwrapObservable(value));
},
update: function (element, valueAccessor) {
var value = valueAccessor();
ko.utils.unwrapObservable(value) ? $(element).fadeIn() : $(element).fadeOut();
}
};
ko.applyBindings(new ViewModel());
HTML:
<div class='list-inputs'>
<h2>Drilldown toggle interaction</h2>
<!--LEVEL 1-->
<div data-bind='fadeVisible: showField1'>(L1) Choose one to display more options:
<p>
<label>
<input type='radio' name="type" value='yes' data-bind='checked: yesOrNo' />Yes</label>
<label>
<input type='radio' name="type" value='no' data-bind='checked: yesOrNo' />No</label>
</p>
<!--LEVEL 2-->
<div data-bind='fadeVisible: showField2'>
<p>(L2) Enter input and click off:
<input type="text" data-bind='value: hasValue' />
</p>
<!--LEVEL 3-->
<div data-bind='fadeVisible: showField3'>
<p><span>(L3) Earnings:</span>
<input type="text" /> <a data-bind="click: toggleCalc" href="#">Toggle calculator</a>
</p>
<!--LEVEL 4-->
<div data-bind='fadeVisible: showField4'>
<br />
<p><b>(L4) Calculator</b>
</p>
<p><span>Input 1:</span>
<input type="text" />
</p>
<p><span>Input 2:</span>
<input type="text" />
</p>
<p><span><b>Total:</b></span>
<input type="text" />
</p>
</div>
</div>
</div>
</div>
</div>
I got this working (I think the way you want) by making two changes:
I initialized the yesOrNo field to "no".
Changed showField2 to clear out the value of the "L2" textbox whenever the user changes their L1 response to "No". This causes the form to "re-initialize" so the next time they select yes, it will start clean.
self.showField2 = ko.computed(function () {
var isNo = self.yesOrNo() == 'no';
if( isNo )
{
self.hasValue('');
}
return !isNo;
});

Why my UI is not update after add an item in a observableArray in Knockout js?

I am new in knockout js. I am trying to learn it. As my learning process i made a sample program. But i face a problem while i add a new item in observableArray. i can successfully add an item in observableArray but after add it does not show any text in my select. But an item is added. When I click that item all information is show below.
my HTML :
<div id="contener">
<div id="productListView">
<select multiple="multiple" id="MyproductListView" size="10" style="min-width: 120px;" data-bind="options: productCollection, value: listViewSelectedItem, optionsText: 'description'"></select>
</div>
<div id="productView" data-bind="with: selectedProduct">
<p>
SKU: <span data-bind="text: sku"></span>
</p>
<p>
Description: <span data-bind="text: description"></span>
</p>
<p>
SKU: <span data-bind="text: price"></span>
</p>
<p>
Description: <span data-bind="text: cost"></span>
</p>
<p>
Description: <span data-bind="text: quantity"></span>
</p>
</div>
<div id="NewProduct" data-bind="with: selectedProduct">
<form>
<fieldset>
<legend>Product Details</legend>
<label>SKU :
<input type="text" data-bind="value:sku" /></label>
<br />
<label>Description :
<input type="text" data-bind="value:description" /></label>
<br />
<label>Price :
<input type="text" data-bind="value:price" /></label>
<br />
<label>Cost :
<input type="text" data-bind="value:cost" /></label>
<br />
<label>Quantity :
<input type="text" data-bind="value:quantity" /></label>
</fieldset>
</form>
</div>
<div id="buttonContainer">
<button type="button" data-bind="click:addNewProduct">Add</button>
<button type="button" data-bind="click:RemoveProduct">Remove</button>
<button type="button" data-bind="click:DoneEditingProduct">Done</button>
</div>
</div>
My Knockout is :
window.myapp = {};
(function (myapp) {
var self = this;
function product() {
self.sku = ko.observable("");
self.description = ko.observable("");
self.price = ko.observable(0.00);
self.cost = ko.observable(0.00);
self.quantity = ko.observable(0);
}
myapp.Product = product;
}(window.myapp));
(function (myApp) {
function productsViewModel() {
var self = this;
self.selectedProduct = ko.observable();
self.productCollection = ko.observableArray([{ sku: 'sku', description: 'des', price: '5.0', cost: '8.0', quantity: '1' }]);
self.listViewSelectedItem = ko.observable(null);
self.addNewProduct = function () {
var p = new myApp.Product();
self.selectedProduct(p);
}.bind(self);
self.DoneEditingProduct = function () {
var p = self.selectedProduct();
if (!p)
return;
if (self.productCollection.indexOf(p) > -1)
return;
self.productCollection.push(p);
self.selectedProduct(null);
self.productCollection.valueHasMutated();
}.bind(self);
self.RemoveProduct = function () {
var p = self.selectedProduct();
if (!p)
return;
return self.productCollection.remove(p);
};
self.listViewSelectedItem.subscribe(function (product) {
if (product) {
self.selectedProduct(product);
}
});
}
myApp.ProductsViewModel = productsViewModel;
}(window.myapp));
(function (myApp) {
function app() {
this.run = function () {
var vm = new myApp.ProductsViewModel();
ko.applyBindings(vm);
};
}
myApp.App = app;
}(window.myapp));
var app = new myapp.App();
app.run();
I already 3 days for it and search a lot but i can`t explore why it is not update.
Must i do something wrong.
Update :
My Code in Fiddle:
http://jsfiddle.net/shuvo009/ReSUL/1/
http://jsfiddle.net/sujesharukil/ReSUL/3/
you are referencing the parent object
function product() {
self.sku = ko.observable("");
self.description = ko.observable("");
self.price = ko.observable(0.00);
self.cost = ko.observable(0.00);
self.quantity = ko.observable(0);
}
change that to
function product() {
this.sku = ko.observable("");
this.description = ko.observable("");
this.price = ko.observable(0.00);
this.cost = ko.observable(0.00);
this.quantity = ko.observable(0);
}
updated fiddle above.

Categories