Knockout JS Computed Observable on edit record - javascript

I have three fields
1. Quantity
2. Unit Price
3. Total Cost
I am using knockout JS to calculate total cost in real time. This works fine when adding a new record. However when I edit the record, I want the quantity and unit price to be prepopulated by their value in the database when the page first loads. I have tried the code below which prepopulates the quantity and unit price but the Total Cost result does not update and appears as blank now. Here is the code
<tr>
<td>
<div class="form-group">
<label asp-for="Quantity" class="col-md-2 control-label"></label>
<div class="col-md-10">
<kendo-numerictextbox name="Quantity" min="0" enable="true" data-bind="value: Quant">
</kendo-numerictextbox>
<span asp-validation-for="Quantity" class="text-danger"/>
</div>
</div>
</td>
</tr>
<tr>
<td>
<div class="form-group">
<label asp-for="UnitPrice" class="col-md-2 control-label"></label>
<div class="col-md-10">
<kendo-numerictextbox name="UnitPrice" min="0" enable="true" data-bind="value: UPrice">
</kendo-numerictextbox>
<span asp-validation-for="UnitPrice" class="text-danger"/>
</div>
</div>
</td>
</tr>
<tr>
<td>
<div class="form-group">
<label asp-for="TotalCost" class="col-md-2 control-label"></label>
<div class="col-md-10">
<span data-bind="text: TotalCost"> </span>
</div>
</div>
<script>
var someJSON = $.getJSON("/Expenses/EditUSExpense", function(data) {});
var parsed = JSON.parse(someJSON);
// Update view model properties
var quantity = parsed.Quantity;
var unitprice = parsed.UnitPrice;
var ViewModel = function(quantity, unitPrice, Quantity, UnitPrice) {
this.Quant = ko.observable(quantity);
this.UPrice = ko.observable(unitPrice);
this.TotalCost = ko.pureComputed(function() {
if (isNaN(this.Quant() * this.UPrice())) {
return 0;
}
return this.Quant() * this.UPrice();
}, this);
};
ko.applyBindings(ViewModel);
</script>
</td>
</tr>
UPDATE
I have modified my code per suggestions below and appear to be cleanly pulling the Quantity and UnitPrice from the database but the computed field TotalCost is still displaying null and when I change the other two parameters the value does not change. Here is my modified code so someone can take a look. I am using the same razor code just changed the javascript.
$.getJSON("/Expenses/EditUSExpense", function (data) {
var parsed = JSON.parse(data);
// Update view model properties
var quantity = parsed.Quantity;
var unitprice = parsed.UnitPrice;
var ViewModel = function (quantity, unitPrice) {
this.Quant = ko.observable(quantity);
this.UPrice = ko.observable(unitPrice);
this.TotalCost = ko.pureComputed(function () {
if (isNaN(this.Quant() * this.UPrice())) {
return 0;
}
return this.Quant() * this.UPrice();
}, this);
};
ko.applyBindings(new ViewModel(quantity, unitprice));
});

In your code, you're creating a function that accepts four arguments:
var ViewModel = function(quantity, unitPrice, Quantity, UnitPrice) {
I don't know why four because in the code you posted you're only using the first two.
So if, you're expecting the first two values as arguments you should do this:
var quantity = parsed.Quantity;
var unitprice = parsed.UnitPrice;
var ViewModel = function(quantity, unitPrice) {
this.Quant = ko.observable(quantity);
this.UPrice = ko.observable(unitPrice);
this.TotalCost = ko.pureComputed(function() {
if (isNaN(this.Quant() * this.UPrice())) {
return 0;
}
return this.Quant() * this.UPrice();
}, this);
};
ko.applyBindings(new ViewModel(quantity, unitprice));

This:
var someJSON = $.getJSON("/Expenses/EditUSExpense", function(data) {});
var parsed = JSON.parse(someJSON);
Should be:
$.getJSON("/Expenses/EditUSExpense", function(data) {
var parsed = JSON.parse(data);
// Rest of code...
});
And this:
ko.applyBindings(ViewModel);
Should be:
ko.applyBindings(new ViewModel(quantity, unitprice));

Related

AutoNumeric change value

I'm trying to make my value to have thousand separators and such, but I don't understand how to use jquery and how it works.
I want to make my value from 1000 to 1,000
and I tried using AutoNumeric like this but failed
var autonumeric = new AutoNumeric.multiple(".form-control");
This is the form where I want to change the value type:
<div class="form-group">
<div class="row">
<div class="col-lg-3">
<label for="total" class="label-control" id="labelsubtotal">Total : </label>
</div>
<div class="col-lg-9">
<input type="text" value="" style="text-align: right;" class="form-control" id="grandTotal" name="grandTotal" disabled>
</div>
</div>
</div>
This is how I import AutoNumeric:
<script src="<?php echo base_url();?>js/autonumeric-next/src/AutoNumeric.js" type="text/javascript"></script>
Basically this is what I want to do :
I want the result format changed into currency format
I succeeded in changing only 1 value, this is what I did:
<script>
$('document').ready(function(){
$(function sum() {
console.log($('.calc'))
var sum = 0.0;
$('.calc').each(function() {
sum += parseInt($(this).text());
});
$("#subTotal").val(sum);
let subTotal = new AutoNumeric("#subTotal");
})();
function calculateSubTotal() {
var subtotal = $("#subTotal").val();
$("#subTotalDiscount").val(subtotal - (Math.round(($("#inputDiscount").val() / 100) * subtotal)));
var subtotal_discount = parseInt($("#subTotalDiscount").val());
$("#subTotalTax").val(Math.round(($("#inputTax").val() / 100) * subtotal_discount));
var subtotal_tax = parseInt($("#subTotalTax").val());
var pph = $("#inputpph").val();
$("#SubTotalpph").val(Math.round(parseInt($("#inputpph").val()*subtotal_discount)));
var subtotal_pph = parseInt($("#SubTotalpph").val());
var grandtotal = subtotal_discount + subtotal_tax + subtotal_pph;
$("#grandTotal").val(grandtotal);
}
})
</script>
I'm lost right now.
Also exists a little bit another way to do it. Do next and look what you'll get(one requirement - you need to give number value only):
JS
$('#grandTotal').on('input',function(){
var number, s_number, f_number;
number = $('#grandTotal').val();
s_number = number.replace(/,/g,'');
f_number = formatNumber(s_number);
console.info(f_number);
$('#grandTotal').val(f_number);
});
function formatNumber(num) {
return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')
}
But, if you want just convert value without changing it dynamically then just add this in your attached *.js:
var number, f_number;
number = $('#grandTotal').val();
f_number = formatNumber(number);
console.info(f_number);
$('#grandTotal').val(f_number);
function formatNumber(num) {
return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')
}

KnockoutJS - Multi select using select 2 showing only 1st select

I'm trying to use the select 2 Multi-select boxes with Knockout JS.
The box displays a list of countries and the user can select multiple countries.
The box is displaying multiple countries correctly as expected, however the observable array is only showing the first entry.
My intention is to get all the selected countries and not the first one.
At first i thought i cant use a select 2 multi select with knockout, however if if i add two for example (MT,NL) the observable shows MT, however if i remove MT it updates to NL (so i dont think thats the issue)
Logic below:
// Class to represent a row in the dpos grid
function DpoItem(address, preferredLanguage, countries)
{
var self = this;
self.address = address;
self.preferredLanguage = preferredLanguage;
self.countries = ko.observableArray(countries);
}
// Class to represent a language
function LanguageItem(code, name)
{
var self = this;
self.code = code;
self.name = name;
}
// Class to represent a country
function CountryItem(code, name)
{
var self = this;
self.code = code;
self.name = name;
}
// Overall viewmodel for this screen, along with initial state
function DposViewModel()
{
var self = this;
// Populate countries
var countriesObject = JSON.parse(countriesJSON);
var countries = [];
for (var cKey in countriesObject)
{
countries.push(new CountryItem(cKey, countriesObject[cKey]));
}
self.countriesList = ko.observableArray(countries);
// Populate languages
var languagesObject = JSON.parse(languagesJSON);
var languages = [];
for (var lKey in languagesObject)
{
languages.push(new LanguageItem(lKey, languagesObject[lKey]));
}
self.languagesList = ko.observableArray(languages);
// parse JSON DTOs and put them in the viewmodel
var dposObject = JSON.parse('[{"countries":[],"type":"dpo","address":"dpo #avis.com","preferredLanguage":"en - GB"},{"countries":["GB", "MT"],"type":"dpo","address":"dpo #avis.co.uk","preferredLanguage":"en - GB"},{"countries":["MT"],"type":"dpo","address":"dpo #avis.com.mt","preferredLanguage":"mt - MT"}]');
var dpos = [];
dposObject.forEach(dpo =>
{
dpos.push(new DpoItem(dpo.address, dpo.preferredLanguage, dpo.countries));
});
self.dpos = ko.observableArray(dpos);
self.addDpo = function ()
{
self.dpos.push(new DpoItem("", "", ""));
};
self.removeDpo = function ()
{
self.dpos.remove(this);
};
self.checkDpos = function ()
{
for (i = 0; i < self.dpos().length; i++)
{
var dpo = self.dpos()[i];
var dpoCountries = dpo.countries();
}
};
}
ko.applyBindings(new DposViewModel());
$(document).ready(function ()
{
$('.js-example-basic-multiple').select2();
});
UI below:
<div id="table" class="table-editable">
<table class="table">
<thead>
<tr>
<th>Email</th>
<th>Preferred Language</th>
<th>Countries</th>
<th><span id="table-add" class="table-add glyphicon glyphicon-plus" data-bind="click: addDpo"></span></th>
</tr>
</thead>
<tbody data-bind="foreach: dpos">
<tr>
<td contenteditable="true" data-bind="text: $data.address"></td>
<td>
<select class="js-example-basic-single" data-bind="options: $parent.languagesList, optionsText: 'name', optionsValue: 'code', value: $data.preferredLanguage"></select>
</td>
<td>
<select class="js-example-basic-multiple" multiple="multiple" data-bind="options: $parent.countriesList, optionsText: 'name', optionsValue: 'code', value: $data.countries"></select>
</td>
<td>
<span class="table-remove glyphicon glyphicon-remove" data-bind="click: $parent.removeDpo"></span>
</td>
</tr>
</tbody>
</table>
</div>
<button data-bind="click: checkDpos">Click me</button>
<div>
<h1>Summary</h1>
<div data-bind="foreach: dpos">
<p>Address: <strong data-bind="text: address"></strong></p>
<p>Preferred Language: <strong data-bind="text: preferredLanguage"></strong></p>
<p>Countries: <strong data-bind="text: countries"></strong></p>
</div>
</div>
Any ideas why this is happening?

Error with Function getting the correct Element ID from loop usingJavaScript

I'm trying to create a functional wages table using html and javascript that calculates the wage for each employee, according to input of each row.
To do this I have generated a different id for the total cell in each row of the table using a loop. The problem is that when i try to access each id in the loop that creates the table, the javascript function uses the last id name of the table created, instead of using the current id from the row that the function is called.
How can i get the correct id using only javascript and html?
This is my html code:
#foreach (var item in Model.CurrentUnpaidWages)
{
<tr>
#{ id = Convert.ToString(Model.Employees[i].PersonID);}
<td>
#Model.Employees[i].FirstName #Model.Employees[i].LastName
</td>
<td id="WagePerHour">
#Html.DisplayFor(modelItem => item.WagePerHour)
#{wagePerHour = Convert.ToDecimal(item.WagePerHour);}
</td>
<td>
#{ id = Convert.ToString(Model.Employees[i].PersonID);}
<div class="form-group">
<div class="col-md-10">
<input oninput="GetWagePerMonth(this)" id=#id type="number"
min="1" />
#Html.ValidationMessageFor(model => model.Wage.HoursPerMonth,
"", new { #class = "text-danger" })
</div>
</div>
</td>
<td><div id="d"#id></div></td>
<td> #Html.DisplayFor(modelItem => item.Bonus)</td>
<td>#Html.DisplayFor(modelItem => item.Paid)</td>
#{i++;}
<td></td>
</tr>
This is my function:
function GetWagePerMonth(idx) {
var h =idx.id;
alert( "d" + idx.id);
var wage = document.getElementById("WagePerHour").innerHTML;
var wagePerHour = #wagePerHour;
var wagePerMonth = wage * wagePerHour;
wagePerMonth = round(wagePerMonth, 2).toFixed(2);
document.getElementById("d" + idx.id).innerHTML = wagePerMonth;
}
Thanks in advance!!
Edit: I've edited my code. See above.
Now I'm getting the correct id each time in an alert, but the function is not changing the text of the div id=#id. The div is staying empty. I would like that for each time I enter an amount into the input of :
then the correct text should be entered into the div id=#id according to the function. For now, nothing is happening.
<input oninput="GetWagePerMonth(this)" id=#id type="number" min="1" />
make <td id=#id"></td > into <div id="d"#id></div >
and your function would access the id as
function GetWagePerMonth(idx) {
alert(idx.id); /* */
var wage = document.getElementById("WagePerHour").innerHTML;
var wagePerHour = #wagePerHour;
var wagePerMonth = wage * wagePerHour;
wagePerMonth = round(wagePerMonth, 2).toFixed(2);
document.getElementById("d"+idx.id).innerHTML = wagePerMonth;
}
USe this instead and see if you get any response.
function GetWagePerMonth(idx) {
alert(idx.id); /* */
document.getElementById("d"+idx.id).innerHTML = "Test Value";
}
Hope this helps you get to a solution that you can improvise
edit:
create a new function
function CallMyFunc(){
for(i=0,i < 10, i++){
GetWagePerMonth(i) ;
}
}
In the HTML part , lets see if you can create a button to call that function
<input type = "button" onClick="javascript:CallMyFunc()"/>

knockoutjs radio button checked value with protectedobservable

thank you for looking into this.
I have the following example built: http://jsfiddle.net/zm381qjx/5/
This is a menu list builder. When you add a menu, an edit form pops up. Using protectedObservable so that i can either commit or reset (as per code). One functionality, which i am having problems with, is there is radio button list (for TypeId), and depending on the value (10 = Url, 20 = Category, 30 = Page), you set the respective properties (10 = Url, 20 = CategoryId, 30 = PageId).
Flicking through the radio buttons, if Url is selected, another textbox should show (based on urlVisible) so user can enter the Url. I have added a span with text: TypeId.temp so i can see the temporary value. This is very irregular. Try to flick through several times.
Any help will be greatly appreciated.
My HTML
<a class="btn btn-primary" data-bind="click: addMenu">Add Menu</a>
<ul data-bind="foreach: Menus">
<li></li>
</ul>
<div class="panel panel-default" data-bind="slideIn: editMenuItem, with: editMenuItem">
<div class="panel-body">
<div class="form-group">
<label for="MenuName">Name: </label>
<input type="text" id="MenuName" data-bind="value: Name" class="form-control" />
</div>
<label class="radio-inline">
<input type="radio" name="MenuTypeId" value="10" data-bind="checked: TypeId" /> Url
</label>
<label class="radio-inline">
<input type="radio" name="MenuTypeId" value="20" data-bind="checked: TypeId" /> Category
</label>
<label class="radio-inline">
<input type="radio" name="MenuTypeId" value="30" data-bind="checked: TypeId" /> Page
</label>
<div class="form-group" data-bind="visible: urlVisible">
<label for="MenuUrl">Url: </label>
<input type="text" id="MenuUrl" data-bind="value: Url" class="form-control" />
</div>
<br />
<p>TypeId.temp = <span data-bind="text: TypeId.temp"></span></p>
<br /><br />
<input type="button" class="btn btn-success" value="Update" data-bind="click: commit" /> or
Cancel
</div>
</div>
My JS:
var vm = null;
//wrapper for an observable that protects value until committed
ko.protectedObservable = function (initialValue) {
//private variables
var _temp = ko.observable(initialValue);
var _actual = ko.observable(initialValue);
var result = ko.dependentObservable({
read: function () {
return _actual();
},
write: function (newValue) {
_temp(newValue);
}
});
//commit the temporary value to our observable, if it is different
result.commit = function () {
var temp = _temp();
if (temp !== _actual()) {
_actual(temp);
}
};
//notify subscribers to update their value with the original
result.reset = function () {
_actual.valueHasMutated();
_temp(_actual());
};
result.temp = _temp;
return result;
};
ko.bindingHandlers.slideIn = {
init: function (element) {
$(element).hide();
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (value) {
$(element).stop().hide().slideDown('fast');
} else {
$(element).stop().slideUp('fast');
}
}
};
var Menu = function (Id, Name, TypeId, CategoryId, PageId, Url) {
var self = this;
/* Core Properties */
self.Id = ko.observable(Id);
self.Name = ko.protectedObservable(Name);
self.TypeId = ko.protectedObservable(TypeId);
self.CategoryId = ko.protectedObservable(CategoryId);
self.PageId = ko.protectedObservable(PageId);
self.Url = ko.protectedObservable(Url);
/* Virtual Properties */
self.urlVisible = ko.computed(function () {
return self.TypeId.temp() == "10";
}, self);
/* Virtual Functions */
self.editMenu = function (data) {
if(vm.editMenuItem()) {
vm.editMenuItem(null);
}
vm.editMenuItem(data);
};
/* Core Functions */
self.commit = function () {
if (self.Name.temp() == '' || self.Name.temp() == null) {
alert('Please enter a name.'); return;
}
self.Name.commit();
self.TypeId.commit();
self.CategoryId.commit();
self.PageId.commit();
self.Url.commit();
vm.editMenuItem(null);
};
self.reset = function () {
self.Name.reset();
self.TypeId.reset();
self.CategoryId.reset();
self.PageId.reset();
self.Url.reset();
vm.editMenuItem(null);
};
};
var ViewModel = function() {
var self = this;
/* Core Properties */
self.Menus = ko.observableArray([]);
/* Virtual Properties */
self.editMenuItem = ko.observable(null);
self.addMenu = function(){
var menu = new Menu(0, "New Menu", "10", 0, 0, "");
self.Menus.push(menu);
self.editMenuItem(menu);
};
};
$(function () {
vm = new ViewModel();
ko.applyBindings(vm);
});
If you change your radio button binding to
<input type="radio" name="MenuTypeId" value="10" data-bind="checked: TypeId.temp" />
The temp id will be changed accordingly and radio button behaviour is consistent, but not with TypeId as value.
also the protectedObservable binding the radio button value is not playing nice
When you manually click the radio the TypeId value is never changed (as you are not committing the value) and I guess that as the radio button value never changes from 10 , it is not recognizing the subsequent manual clicks on Url radio button.
I updated the value using a button and it is changing accordingly; but then it will not move the value from that TypeId on subsequent radio button clicks
And the problem is still appearing for protectedObservable binding but not with a simple observable.
Code which explores this idea further: http://jsfiddle.net/zm381qjx/101/

ko.subscribe on child model properties

Looking for a good example of how to set up child models in knockoutjs. This includes binding to child events such as property updates which I haven't been able to get working yet.
Also, it would be better to bind to a single child in this case instead of an array but I don't know how to set it up in the html without the foreach template.
http://jsfiddle.net/mathewvance/mfYNq/
Thanks.
<div class="editor-row">
<label>Price</label>
<input name="Price" data-bind="value: price"/>
</div>
<div class="editor-row">
<label>Child</label>
<div data-bind="foreach: childObjects">
<div><input type="checkbox" data-bind="checked: yearRound" /> Year Round</div>
<div><input type="checkbox" data-bind="checked: fromNow" /> From Now</div>
<div>
<input data-bind="value: startDate" class="date-picker"/> to
<input data-bind="value: endDate" class="date-picker"/>
</div>
</div>
</div>
var ChildModel= function (yearRound, fromNow, startDate, endDate) {
var self = this;
this.yearRound = ko.observable(yearRound);
this.fromNow = ko.observable(fromNow);
this.startDate = ko.observable(startDate);
this.endDate = ko.observable(endDate);
this.yearRound.subscribe = function (val) {
alert('message from child model property subscribe\n\nwhy does this only happen once?');
//if(val){
// self.startDate('undefined');
// self.endDate('undefined');
//}
};
}
var ParentModel = function () {
var self = this;
this.price = ko.observable(1.99);
this.childObjects = ko.observableArray([ new ChildModel(true, false) ]);
};
var viewModel = new ParentModel ();
ko.applyBindings(viewModel);
Try it with the following:
this.yearRound.subscribe(function (val) {
alert('value change');
});
If you want to have the subscriber also being called while loading the page do something like this:
var ChildModel= function (yearRound, fromNow, startDate, endDate) {
var self = this;
this.yearRound = ko.observable();
this.fromNow = ko.observable(fromNow);
this.startDate = ko.observable(startDate);
this.endDate = ko.observable(endDate);
this.yearRound.subscribe(function (val) {
alert('value change');
});
this.yearRound(yearRound);
}
http://jsfiddle.net/azQxx/1/ - this works for me with Chrome 16 and Firefox 10
Every time the checked button changes its value the callback fires.
The observableArray is fine in my opinion if you may have more than one child model associated to the parent.

Categories