Knockout.js computed - javascript

I am using Knockout.js to develop a calculator. I am facing a situation that I don't know how to handle.
In the below example, everything works fine when the user enters a product and value and the values are saved in the DB. But when the user comes to edit this page, my requirement is to show all the values the user entered along with the total. But when the user changes something in the product or quantity, the total should change as expected.
We now store the total in DB and total could be changed by some admin users.In this case,is there a way we can override the computed value when the user goes to the edit page but when the user changes product or quantity,then the compute should happen with the latest values from product and quantity.
Help is appreciated.
var TsFoundationDeviceModel = function(product,qty) {
var self = this;
self.product = ko.observable();
self.quantity= ko.observable();
self.computedExample = ko.computed(function() {
return self.product() * self.quantity() ;
});}
My HTML code looks like
<input name="product" data-bind="value:product">
<input name="value" data-bind="value:value">
<input name="total" data-bind="value:computedExample"/>

You have to bind the view to the screen. Check out this fiddle and give it
var TsFoundationDeviceModel = function(product,qty) {
var self = this;
self.product = ko.observable(product);
self.quantity= ko.observable(qty);
self.computedExample = ko.computed(function() {
return self.product() * self.quantity() ;
});}
ko.applyBindings(new TsFoundationDeviceModel(2,3));
http://jsfiddle.net/Z6VPV/5/

When you reference observables within your computed you need to treat them like functions so instead of return self.product * self.quantity ; you would use return self.product() * self.quantity();
Fiddle: http://jsfiddle.net/SAV9A/

Related

Knockout.js boolean not updating

I'm attempting to enable / disable an input field.
<input data-bind="disable: chatDisabled" id="send-message" type="textarea" class="input-area" value="">
And my knockoutJS
function MessagesViewModel() {
var self = this;
var socket = io.connect('http://127.0.0.1:4000');
self.messages = ko.observableArray([]);
self.chatSend = ko.observable();
self.questionChoice = ko.observable();
self.chatDisabled = ko.observable(false);
socket.on('receiveMessages', function(data) {
self.messages(data.messages);
var last = data.messages[data.messages.length-1];
self.chatDisabled = last.enforce || false;
scrollToBottom();
});
}
ko.applyBindings(new MessagesViewModel());
I've managed to get other parts working such as having a list of messages on the screen populate when the array is updated. However for the life of me I cannot get the input to toggle between disabled / enabled when it is changed within socket.on(
self.messages( updates corrected so why does self.chatDisabled not? (To be clear the variable JS side is updated however it the data-bind is not.
This should do the trick:
self.chatDisabled(last.enforce || false);
You were assigning a new value to your property instead of updating the observable value.

angularjs Nan Is Appearing if textbox is empty

I am creating a web app in which I am calculating a $scope variable with a textbox, but if a user put textbox empty or if a user don't enter anything on textbox the result is coming NaN here is my code
<input type="text" ng-model="amount1" ng-blur="amountchange(amount1)" />
{{total}}
And in my controller I have
$scope.amountchange = function () {
$scope.total = parseInt($scope.amount1) + parseInt($scope.total);
}
I want to get rid off the NaN which is apearing if the textbox is empty
Here is a fiddle that I created for better understanding
CLICK HERE
Use
$scope.constant = 200; //Define other variable to persist constants
$scope.total = $scope.constant; //Initialize total
$scope.amountchange = function() {
var amount1 = parseInt($scope.amount1, 10) || 0; //parse amount if its invalid set it to zero
$scope.total = $scope.constant + amount1; //update total
}
DEMO

Make knockout listen to values changes which are not a result of a keystroke [duplicate]

This question already has an answer here:
Knockout.js bound input value not updated when I use jquery .val('xyz')
(1 answer)
Closed 6 years ago.
self.totalHours = ko.pureComputed(function() {
var start=self.num1;
var end=self.num2;
return start+end;
});
<input type="text" data-bind="textInput: start">
<input type="text" data-bind="textInput: end">
<input type="text" data-bind='text: totalHours()'>
The above first is part of my viewmodel and the second is part of my model. num1,num2 are observables. Every time I change manually the value inside the above first two inputs the third input is updated immediately; however, when the values change by code, knockout does not listen to the changes and total is not updated. How may I oblige knockout to listen to the changes provoked by code?
Quite some stuff you can fix and improve here:
A computed value will re-evaluate when an observable it uses in its method changes: self.num1 and/or self.num2 need to be observable and evaluated using ()
If you want to bind an <input>'s value, you have to use either the value or textInput data-bind; the text bind will not work.
If you want to write to a computed, you'll have to specify a write method. You'll have to tell knockout how to update the computed's dependencies to make sure all values add up. (e.g.: setting totalHours could set num1 to totalHours and num2 to 0)
You've bound to start and end, while your viewmodel properties are named num1 and num2.
When using value or textInput, user input will be returned as a string. You'll need to parse the strings to numbers if you want to use them in any math.
Now that all code should be working correctly, you can update your viewmodel's values via the inputs, or via code:
var ViewModel = function() {
var self = this;
self.num1 = ko.observable(0);
self.num2 = ko.observable(0);
self.totalHours = ko.pureComputed(function() {
var start = parseFloat(self.num1());
var end = parseFloat(self.num2());
return start + end;
});
};
var vm = new ViewModel();
ko.applyBindings(vm);
// Updating your values from code:
vm.num1(1);
vm.num2(2);
// Whenever the values need to be updated via js,
// you should change the source values, _not_ the
// <input>'s values. Worst case scenario, you'll
// have to do something like this:
var updateNum1ViaDOM = function() {
ko.dataFor(document.querySelector("input")).num1(5);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input type="text" data-bind="textInput: num1">
<input type="text" data-bind="textInput: num2">
<span data-bind='text: totalHours'></span>
Note: it's probably better to use an extender to force num1 and num2 to be numeric: Live Example 1: Forcing input to be numeric
Not sure if it is a copy paste problem but the the code you posted will not work as intended. I've updated the example, when changing an observable value it must be passed as parameter so as not to overwrite the knockout observable
self.start = ko.observable();
self.end = ko.observable();
self.totalHours = ko.computed(function() {
return self.start() + self.end();
});
<input type="text" data-bind="textInput: start">
<input type="text" data-bind="textInput: end">
<input type="text" data-bind='text: totalHours()'>
//Then when changing the value by code
var newValue = 42;
model.start(newValue); //assuming you are making the change outside your viewmodel
*Just noticed this code will throw an exception when you edit the input bound to totalHours as it does not have a write handler defined. This is a separate issue though.

How can I get these two javascript codes to work together?

I made a form using html.
At first I had it really simple. My input was amount, which a user would enter. I then made javascript code to calculate a dynamic price based on the user's amount input. The code is as follows:
<input class="typeahead" type="text" placeholder="Amount" name="Gift-Card Amount"/>
The javascript:
jQuery("input[name='Gift-Card Amount']").change(function () {
if (isNaN(parseFloat(this.value)) || !isFinite(this.value)) {
jQuery(this).val('');
return false;
}
var calc = parseFloat(this.value) * 0.95;
jQuery(this).parents("form").find("input[name='price']").val(calc);
});
The calculation is a constant 0.95. So I added a new input. Store name. So the user could enter the store name. The amount:
<input class="stores typeahead" type="text" placeholder="Stores" name="name"/>
And I want price to change based on both store name and amount. So I created this object:
var stores = {
"McDonalds" : .90,
"Target" : .92,
}
var storeName = jQuery(this).parents("form").find("input[name='name']").val();
console.log(stores[storeName]);
So that instead of a constant 0.95, that value can be replaced with preset values based on the store name entered. I don't know how to get those two to work together. Meaning, how do I recode the first javascript to recornize the var store values instead of 0.95?
jQuery("input[name='Gift-Card Amount']").change(function () {
var amount = parseFloat(this.value);
if (isNaN(amount) || !isFinite(amount)) {
jQuery(this).val('');
return false;
}
var storeName = jQuery(this).parents("form").find("input[name='name']").val();
if (storeName in stores) {
var calc = amount * stores[storeName];
jQuery(this).parents("form").find("input[name='price']").val(calc);
}
});
I also suggest that you change Stores from a text input to <select>. That way, you don't depend on the user spelling the store correctly, including capitalization.
<select name="name" class="storeName">
<option value="">Please select a store</option>
<option value=".90">McDonalds</option>
<option value=".92">Target</option>
</select>
Then you can use
var calc = parseFloat(jQuery(this).parents("form").find(".storeName").val());
I would do it like the following:
function calcPrice(element) {
// Use the element that called the listener to find the form
var form = element.form;
// Access form controls as named properties of the form
var amt = form["Gift-Card Amount"].value;
// Don't clear the value if it's not suitable, it's annoying
// Let the user fix it themselves
if (parseFloat(amt) != amt) {
alert("Gift card amount is not a suitable value")
}
// Set the price
form.price.value = amt * form.store.value;
}
</script>
Sample form, which has all the store values. This way, you can have a fallback where the server gets all relevant values, you aren't dependent on client side calculations..
<form>
Store:
<select name="store" onchange="calcPrice(this)">
<option value="0.90">McDonalds
<option value="0.92">Target
</select>
<br>
Gift card amount:
<input name="Gift-Card Amount" onchange="calcPrice(this)">
Price:
<input name="price" readonly>
</form>
write a little function that may return the correct value
function retrieveStoreValue(store){
return stores[store];
} // be sure stores is defined before the first call of this one
and in your change function call it
var storeName = jQuery(this).parents("form").find("input[name='name']").val();
var calc = parseFloat(this.value) * (retrieveStoreValue(storeName) );

Strange jquery bug in simple code

I have a simple html code with form:
<span class="price"></span>
Enter amount:
<input type="text" class="form-control amount" name="amount" value="500">
<!--Next input fields are hidden by Bootstrap class "hide"-->
<input type="text" name="minimal-amount" class="hide minimal-amount" value="500">
<input type="text" name="oneprice" class="hide oneprice" value="0.20">
<script>
$(".amount").on("change", function(){
var am = $(".amount").val();
var min = $(".minimal-amount").val()
if(am<min){
$(".amount").val($(".minimal-amount").val());
}else{
var am = $(".amount").val();
var oneP = $(".oneprice").val();
var finalPrice = am*oneP;
$(".price").html(finalPrice);
}
});
</script>
Idea of this code is very simple. When user put in amount field digits, my script should check, if that, what user put is smaller than minimum available value in minimal-amount field, script changes value of amount field to default minimal-amount.
But the problem is, that id I just add 0 in amount field (and it's value become 5000) everything is ok, but when I changes value of amount field to 1000, script changes value of amount field to default, as if it smaller them minimul-amount.
What I do wrong, and how can I fix this problem?
P.S. Example of this code you can find here - http://friendfi.me/tests/amount.php
You should parse the value before use. Because .val() will return only string type.
$(".amount").on("change", function(){
var am = parseFloat($(".amount").val());
var min = parseFloat($(".minimal-amount").val());
if(am<min){
$(".amount").val($(".minimal-amount").val());
}else{
var am = $(".amount").val();
var oneP = $(".oneprice").val();
var finalPrice = am*oneP;
$(".price").html(finalPrice);
}
});
There are a lot of gotchas in that code. Here is a working JSBin: http://jsbin.com/qilob/2/edit?html,js,output
Highlights
You need the DOM to be initialized before you can work with it.
Wrapping this in a function passed to jQuery will make it wait till
the page finishes loading before manipulating it.
$(function() { ... });
Use cached values since the elements are not going to change much.
This saves the need to parse the selectors multiple times. It also saves
on typing and readability.
var $amount = $("#amount");
var $minimalAmount = $("#minimal-amount");
var $onePrice = $("#oneprice");
var $finalPrice = $("#price");
When parsing a string to an Int you need to use parseInt
var amount = parseInt($amount.val(), 10);
Conversely when parsing a string to a Float you need to use parseFloat
var price = parseFloat($onePrice.val());
JavaScript can not handle float based arithmetic well.
rounding errors are bad especially when dealing with money we need
to move the decimal place to prevent rounding errors in the more significant
parts of the price value.
var total = (amount * (price * 100)) / 100;
See it in action in the JSBin.

Categories