How to fix javascript .toFixed is not a Function error - javascript

Here I am trying to add two decimal values in line var totalSum = (grandTotal + getShippingCost).toFixed(3); and put the value in var getSumTd = $("tr#sumTr").find("span#sumSpan");.But the problem is that var totalSum = (grandTotal + getShippingCost).toFixed(3); throws an error saying Uncaught TypeError: value.toFixed is not a function.
Any help with my code will be great help.
Below is my script
<script>
$('button#value-plus').on('click', function () {
debugger;
var divUpd = $(this).closest("tr").find('#qnty');
var subtotalcontainer = $(this).closest("tr").find('span#subtotal');
var mainGrandTotalcontainer = $("tr#mainGtTr").find("#mainGt");
var mainGtVal = parseFloat($("tr#mainGtTr").find('span#shippingCost').text());
var getSumTd = $("tr#sumTr").find("span#sumSpan");
var getShippingCost = parseFloat($("tr#mainGtTr").find('span#mainGt1').text());
var bklId = $(this).closest("tr").find('#pid').val();
var url = "/Product/incrementcart";
$.getJSON(url, { prdid: bklId }, function (data) {
debugger;
divUpd.val(data.qty);
var subTotal = data.qty * data.price;
subtotalcontainer.text(subTotal.toFixed(2));
var grandTotal = (mainGtVal + data.price).toFixed(3);
mainGrandTotalcontainer.text(grandTotal);
var totalSum = (grandTotal + getShippingCost).toFixed(3);
getSumTd.text(totalSum);
}).success(function () {
debugger
var url = "/Product/cartupdate";
$.get(url, function (data) {
debugger;
$(".shopping_button").html(data);
})
});
});
Below is my HTML
<tbody>
#foreach (var item in Model)
{
<tr>
#Html.HiddenFor(model => item.ProductId, htmlAttributes: new { #id = "pid" })
<td data-title="Product Image & name" class="t_md_align_c">
<img src="images/quick_view_img_10.jpg" alt="" class="m_md_bottom_5 d_xs_block d_xs_centered">
#Html.DisplayFor(modelItem => item.ProductName)
</td>
<td data-title="Stock">
#Html.DisplayFor(modelItem => item.Instock)
</td>
<td data-title="Price">
<p class="f_size_large color_dark">$#Html.DisplayFor(modelItem => item.ProductPrice)</p>
</td>
<td data-title="Quantity">
<div class="clearfix quantity r_corners d_inline_middle f_size_medium color_dark m_bottom_10">
<button class="bg_tr d_block f_left" data-direction="down" id="value-minus">-</button>
<input type="text" name="" id="qnty" readonly value="#item.Quantity" class="f_left">
<button class="bg_tr d_block f_left" data-direction="up" id="value-plus">+</button>
</div>
</td>
<td data-title="Subtotal">
<p class="f_size_large fw_medium scheme_color">$<span id="subtotal">#Html.DisplayFor(modelItem => item.Total)</span></p>
</td>
<td data-title="Remove">
<i class="fa fa-times f_size_medium m_right_5"></i>Remove<br>
</td>
</tr>
}
<tr id="mainGtTr">
<td colspan="4" class="v_align_m d_ib_offset_large t_xs_align_l">
<div class="d_ib_offset_0 d_inline_middle half_column d_xs_block w_xs_full m_xs_bottom_5">
<button class="button_type_6 bg_scheme_color f_size_large r_corners tr_all_hover color_light m_bottom_20">Check Out </button>
</div>
<p class="fw_medium f_size_large t_align_r scheme_color p_xs_hr_0 d_inline_middle half_column d_ib_offset_normal d_xs_block w_xs_full t_xs_align_c">Grand Total:</p>
</td>
<td colspan="2" class="v_align_m">
<p class="fw_medium f_size_large scheme_color m_xs_bottom_10">$<span id="mainGt">#ViewBag.SubTotal</span></p>
<p style="font-style:oblique">Include <i class="fa fa-rupee"></i> <span id="shippingCost">#ViewBag.ShipingCost</span> shipping cost</p>
</td>
</tr>
#{
var sum = ViewBag.SubTotal + ViewBag.ShipingCost;
}
<tr id="sumTr">
<td>
<span id="sumSpan">#sum</span>
</td>
</tr>
</tbody>

toFixed() method formats a number. The current value is of type string and instead of arithmetic addition, string concatenation is happening. Convert those to number before adding:
Change:
var totalSum = (grandTotal + getShippingCost).toFixed(3);
To
var totalSum = (Number(grandTotal) + Number(getShippingCost)).toFixed(3);

Only float, int value have toFixed. controle your variable and see which type are they.
console.log(("4" + 5).toFixed(3)); // error
console.log((5 + 5).toFixed(3)); // yeep its working

toFixed method is not available on non-number values. you need to parse value to Number first than you can use toFixed method.
let str = `123.123456`
console.log(Number(str).toFixed(3))
console.error(str.toFixed(3))

Check the data type of both the variables. They should be numeric and not strings. The method toFixed will not work for other data types. Also make sure when you convert the string to number, the value in string is internally a number like ‘22’ and not ‘hello’ as converting it to number may give you NaN and your program may fail.

.toFixed() is only a function of a number and returns a string. By using toFixed in multiple assignments, you're turning a number into a string, concatenating multiple strings together, then trying to do a numerical operation on a string.
The following code will give an error.
var grandTotal = (mainGtVal + data.price).toFixed(3); // grandTotal is a string
var totalSum = (grandTotal + getShippingCost).toFixed(3); // grandTotal + getShippingCost is a String, which doesn't have the toFixed function
If you need to avoid floating-point errors, then convert the string to a number before adding it to another number, for example:
var grandTotal = (mainGtVal + data.price).toFixed(3);
grandTotal = Number.parseFloat(grandTotal);
var totalSum = (grandTotal + getShippingCost).toFixed(3);
Otherwise, wait until you're done with your calculations to use toFixed to round to the number of decimal places you want to display, for example:
var grandTotal = mainGtVal + data.price;
var totalSum = (grandTotal + getShippingCost).toFixed(3);
grandTotal = grandTotal.toFixed(3);

Related

How to list items in a function?

Given a list of <a>s like this:
<tr>
<th style="padding-right:1em">Location</th>
<td>
<span class="location">Lower reaches of the Geum River, Korea</span>
</td>
</tr>
JS
function countryList(string) {
let pattern = new RegExp('^.+\/wiki\/'); // adjust `RegExp`
countryListLinks = string.replace(pattern, '');
}
I tried this but I get nothing:
countryLinks.each(function(){
console.log(countryList);
});
I tried this but I only get one item and the other undefined
countryLinks.forEach(countryList);
I am trying to output each href but without /wiki/ individually so that I can use them:
Geum_River Korea
Use map function
function countryList(string) {
let pattern = new RegExp('^.+\/wiki\/'); // adjust `RegExp`
return string.replace(pattern, '');
}
var result = Array.prototype.slice.call(document.getElementsByTagName('a')).map(function(a){return a.href}).map(countryList);
console.log(result)
<tr>
<th style="padding-right:1em">Location</th>
<td>
<span class="location">Lower reaches of the Geum River, Korea</span>
</td>
</tr>
Try this:
let word = '/wiki/';
$('[href]').map(function(i, e) {
var href = $(e).attr('href');
console.log(href.substring(href.indexOf(word) + word.length, href.length));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<tr>
<th style="padding-right:1em">Location</th>
<td>
<span class="location">Lower reaches of the Geum River, Korea</span>
</td>
</tr>
follow this simple code and then parse using replace method
$('.location').find('a').each(function() {
console.log($(this).attr('href'));
});

Assign a Value to a HTML Table Cell using JQuery

There is a table displaying model entries, with each field designated a unique div id combining a keyword and each row's ID. When the user enters a number in the table's input column, a script is supposed to: get the locations of the cells on the same row; and change the values of two predetermined cells based on the values of the other cells.
It seems that tests are successful until the final updating. I've tried using .val(), .value, and .html(), and the resultant cells go blank, or show 0 if the script is error-free. Would someone please post the correct jQuery command and why it works? Many thanks in advance.
The table:
<table id="dt_Positions" class="table table-striped">
<thead>
<tr>
<th class="text-center">Month</th>
<th class="text-center">Owed</th>
<th class="text-center">Bought</th>
<th class="text-center">Total Position</th>
<th class="text-center">Non-Fixed</th>
<th class="text-center">Fixed</th>
<th class="text-center">Fixed Position</th>
<th class="text-center">Proposed</th>
</tr>
</thead>
<tbody>
#if (Model.Forecasts.Any())
{
foreach (var record in Model.Summaries)
{
<tr>
<td id="nmonth#(record.fID)" align="center">#String.Format("{0:d}", #record.Month)</td>
<td id="ntotal#(record.fID)" align="center">#record.NTotal</td>
<td id="nbought#(record.fID)" align="center">#record.NBought</td>
<td id="ntposition#(record.fID)" align="center">#record.NTotalPosition</td>
<td id="nvariable#(record.fID)" align="center">#record.NVariable</td>
<td id="nfixed#(record.fID)" align="center">#record.NFixed</td>
<td id="nfposition#(record.fID)" align="center">#record.NFPosition</td>
<td id="ninput#(record.fID)" align="center"><input class="nInput" type="number" name="quantity" min="1" max="50000"></td>
</tr>
}
}
</tbody>
</table>
The script:
#section Scripts
{
<script src="~/Scripts/jquery-2.1.3.js"></script>
<script type="text/javascript" language="javascript">
$(function () {
$('[id^=ninput]').keyup(function (e) {
var $id = $(this).attr('id');
var $i = $(this);
var $idNum = $id.slice(6);
var $tp = $('#ntposition' + $idNum);
var $fp = $('#nfposition' + $idNum);
var $nt = $('#ntotal' + $idNum);
var $nh = $('#nbought' + $idNum);
var $f = $('#nfixed' + $idNum);
//The lines below appear to be the hiccup
$tp.val($nh.val() + $i.html() - $nt.val());
$fp.val($nh.val() + $i.html() - $f.val());
debugger;
});
});
</script>
}
EDIT: Examples of ids returning "NaN" are:
ntotal = 29, nbought = 5, ntposition = -24, nvariable = 3, nfixed = 26, nfposition = -21, with all appearing to be int from testing the View, but ntotal, nbought, and nfixed showing "NaN" in the console.log and resulting in "NaN" appearing in the test View after an ninput = 5.
$i is the textbox, so to get its value you need to use $i.val(). The other elements are table cells, so to get or set the values you need .text(), not .val(). However you over complicating code by using id attributes. Instead, remove then and use relative selectors
$('input').keyup(function() { // or $('.nInput').keyup
var i = Number$(this).val());
var cells = $(this).closest('tr').children('td');
var tp = cells.eq(3);
var fp = cells.eq(6);
// Get current cell values as a number
var nt = Number(cells.eq(1).text());
var nh = Number(cells.eq(2).text());
var f = Number(cells.eq(5).text());
// Update totals
tp.text(nh + i - nt);
fp.text(nh + i - f);
});
Side note: The value of var i = $(this).val(); could be null but not sure how you want to handle this - possibly just use
var i = $(this).val();
if (!i) {
return; // don't do any calculations
}
You need to know the difference between val(), text() and html()
val() is for getting and setting values for form elements, input, select etc.
text() is for getting and setting plain unformatted text for non form elements.
html() is for getting and setting inner Html from a node
So what you want is:
$tp.text($nh.text() + $i.val() - $nt.text());
$fp.text($nh.text() + $i.val() - $f.text());
Also be careful as + is both mathematical addition and string concatenation in javascript so you may want to cast your parse the strings to the appropriate number type.

update value in cart

I am developing shopping cart application in MVC 4 where I need to update the amount on changing the cart quantity.
#foreach (var item in Model)
{
<tr>
<td>#item.ProductId</td>
<td>#item.Product.ProductName</td>
<td id="PriceBx">#item.Product.UnitPrice</td>
<td id="QtyBx" oninput="calculate()">#Html.TextBox("QuantityBox", item.Quantity, new { style = "width:50px" })</td>
<td id="result">#String.Format("{0:c}", Convert.ToDouble(item.Quantity) * Convert.ToDouble(item.Product.UnitPrice))</td>
</tr>
}
In this I need to update the total when the value in QuantityBox is changed.
I tried using Javascript
<script type="text/javascript">
function calculate()
{
var myBox1 = document.getElementById('QtyBx').value;
var myBox2 = document.getElementById('PriceBx').value;
var result = document.getElementById('result');
var myResult = myBox1 * myBox2;
result.innerHTML = myResult;
}
Generating multiple elements using a foreach then using id attribute for elements inside it is never a good idea because element id has to be unique per HTML page.
Try to append the product-id to the element id:
#foreach (var item in Model)
{
<tr>
<td>#item.ProductId</td>
<td>#item.Product.ProductName</td>
<td id="PriceBx#(item.ProductId)">#item.Product.UnitPrice</td>
<td id="QtyBx#(item.ProductId)" oninput="calculate(#(item.ProductId))">#Html.TextBox("QuantityBox", item.Quantity, new { style = "width:50px" })</td>
<td id="result#(item.ProductId)">#String.Format("{0:c}", Convert.ToDouble(item.Quantity) * Convert.ToDouble(item.Product.UnitPrice))</td>
</tr>
}
And in your Javascript:
function calculate(itemId)
{
var myBox1 = parseInt(document.getElementById('QtyBx' + itemId).value, 10);
var myBox2 = parseFloat(document.getElementById('PriceBx' + itemId).value);
var result = document.getElementById('result' + itemId);
var myResult = myBox1 * myBox2;
result.innerHTML = myResult;
}
(I took the liberty to explicitly convert the values of your inputs to int and float respectively)
first of all, some remarks:
you're using an id in your HTML and you're repeating it through your Model, this breaks the rule that one page should have unique id's
You're not using any javascript framework, though pure javascript is a way to accomplish what you need, but in the future you might have some cross-broser issues when performing more advance tasks
you have an onInput in your td, but it should be in the checkbox it self
you can easily use your own textbox markup:
<td id="QtyBx" oninput="calculate()">
#Html.TextBox("QuantityBox", item.Quantity, new { style = "width:50px" })
</td>
<td id="result">
...
</td>
change to:
<td class="quantity">
<input type="number" id="QuantityBox_#item.Product.ProductId"
value="#item.Quantity"
data-unitprice="#item.Product.UnitPrice"
data-productid="#item.Product.ProductId"
onchange="calculate(this)" />
</td>
...
and, using jQuery (to handle the data- easier) should be something like:
function calculate(elm) {
var chk = $(elm), // the checkbox
vlu = chk.val(), // the current qty value
pid = chk.data("productid"), // product id
unt = chk.data("unitprice"), // unit price
res = $(".result_" + pid), // the result for this product
tot = vlu * unt; // total
res.text("$" + tot); // write the value
}
a live example: https://jsbin.com/gafaja/edit?html,js,output
if you still wanna learn/do it in plain javascript:
function calculate(elm) {
var vlu = elm.value, // the current qty value
pid = elm.getAttribute("data-productid"), // product id
unt = elm.getAttribute("data-unitprice"), // unit price
res = document.getElementsByClassName("result_" + pid), // the result for this product
tot = vlu * unt; // total
res[0].innerHTML = "$" + tot; // write the value
}
one more thing...
don't add style to the elements, just add a stylesheet as:
<style>
.quantity input { width:50px; }
</style>

Need regex filtering with angular-smart-table

I'm using angular smart table. Smart table has built in filtering using the st-search directive. However, I need some customized filtering using regular expressions.
I tried applying an angular filter to my data source (via the ng-repeat directive). While this works in regards to filtering, because smart table isn't aware of what i'm doing, it throws my paging off.
I've plunked an example of what is going on. (Try entering 1.8 in the account filter input box. You'll see the filter get applied but then if you click other pages, you'll see that they contain some of the filtered items as well.) I need the behavior to work similar to what happens if you filter by description (where filtered items get narrowed down and re-paged).
Here is my html table:
<table st-table="vm.accounts" class="table table-striped table-hover table-condensed">
<thead>
<tr>
<th st-sort="account">Account</th>
<th st-sort="desc">Description</th>
<th>Current Balance</th>
<th> </th>
<th> </th>
</tr>
<tr class="warning">
<th>
<input placeholder="filter accounts" class="form-control input-sm" type="search" ng-model="vm.accountfilter" />
</th>
<th>
<input placeholder="filter description" class="form-control input-sm" type="search" st-search="desc" />
</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in vm.accounts | filter:vm.myfilter" ng-click="vm.selected = item" ng-class="{info: vm.selected == item}">
<td>{{item.account}}</td>
<td>{{item.desc}}</td>
<td>{{item.runbal | currency}}</td>
<td><button class="btn btn-danger btn-xs"><i class="fa fa-times-circle"></i></button></td>
<td><button class="btn btn-primary btn-xs"><i class="fa fa-pencil"></i></button></td>
</tr>
</tbody>
<tfoot>
<tr class="warning">
<td colspan="5" class="text-center">
<button class="btn btn-success btn-sm"><i class="fa fa-plus-circle"></i> Add new account</button>
</td>
</tr>
<tr>
<td colspan="5" class="text-center">
<div st-pagination="" st-items-by-page="20" st-displayed-pages="10"></div>
</td>
</tr>
</tfoot>
</table>
and here is the filter that I'm trying to apply (from my controller):
(function(){
'use strict';
angular.module("myapp", ["smart-table"])
.controller("mycontroller", MyController);
function MyController(){
var me = this;
me.accounts = [];
me.selected = null;
me.myfilter = myFilter;
me.accountfilter = '';
activate();
function activate(){
for(var x = 0; x < 6000; x++)
{
var build = '';
for(build; build.length < (12 / x.toString().length); build += x.toString()){}
var aclass = Math.floor((1800 - 1100 + 1) * Math.random() + 1100).toString();
var adept = Math.floor((800 - 100 + 1) * Math.random() + 100).toString();
var aincex = Math.floor(1001 * Math.random() + 4000).toString();
var asub = Math.floor(2 * Math.random());
var account = aclass + adept + aincex + (asub ? "AB" + x.toString() : "");
me.accounts.push({account: account, desc: "Text for " + x + " Account", runbal: x * 5, begbal: 5000, newbegbal: 20000, newrunbal: x * 7});
}
}
function myFilter(value, index, array){
if(!me.accountfilter) return true;
var valex = new RegExp("^[*0-9a-zA-Z.]{1,22}$");
if(me.accountfilter.match(valex))
{
var filter = me.accountfilter;
debugger;
filter = filter.replace(/\*/g,'\\w+');
filter = "^" + filter + ".*$";
var regex = new RegExp(filter,'i');
return value.account.match(regex)
}
else
return false;
}
}
})();
How can I "smart table enable" my filter?
My suspicions were correct. I'm not sure if this is the best way to do it, but here is how I accomplished the special filtering.
I made 3 change to my html.
I added st-pipe to my <table> element.
<table st-pipe="vm.pipe" st-table="vm.accounts" ... >
I removed the angular filter from my ng-repeat.
<tr ng-repeat="item in vm.accounts" ... >
I used the smart-table search feature on the account column (in place of the angular filter that I removed).
<input st-search="account" ... />
In my controller I then added the following:
...
var internalList = [];
me.pipe = Pipe;
function Pipe(tableState){
var perpage = tableState.pagination.number;
var start = tableState.pagination.start;
var filtered = internalList;
//If user has entered filter criteria
if(tableState.search.predicateObject)
{
//clone the filter criteria object
var myPred = $.extend({},tableState.search.predicateObject);
//remove the account criteria so I can process that myself
delete myPred["account"];
//perform the default filter function for any other filters
filtered = $filter('filter')(filtered, myPred);
//if user entered account (regex) filter then call my original filter function
if(tableState.search.predicateObject["account"])
{
filtered = $filter('filter')(filtered,myFilter);
}
}
//apply any sorting that needs to be applied
if (tableState.sort.predicate) {
filtered = $filter('orderBy')(filtered, tableState.sort.predicate, tableState.sort.reverse);
}
//set the bound array to the filtered contents
me.accounts = filtered.slice(start, start +perpage);
//clear the selected item if it is not included on the current page
if(me.accounts.indexOf(me.selected) < 0)
me.selected = null;
//Set the proper number of pages for our filtered list
tableState.pagination.numberOfPages = (filtered.length ? Math.floor(filtered.length / perpage) : 0);
}
Lastly, I had to change the activate function of my controller to populate the internalList rather than the display/bound list.
//me.accounts.push({account: account, desc: "Text for " + x + " Account", runbal: x * 5, begbal: 5000, newbegbal: 20000, newrunbal: x * 7});
internalList.push({account: ...});
You can see it in action here.

how to add an array to localstore in angularjs

I am trying to build a shopping cart. I want to add the array invoice to localstorage so that i could access it later.
I guess there are some errors with this form of approach
angular.module('myApp', ['ngCookies']);
function CartForm($scope, $cookieStore) {
$scope.invoice.items = $cookieStore.get('items');
$scope.addItem = function() {
$scope.invoice.items.push({
qty: 1,
description: '',
cost: 0
});
$scope.invoice.items = $cookieStore.put('items');
},
$scope.removeItem = function(index) {
$scope.invoice.items.splice(index, 1);
$scope.invoice.items = $cookieStore.put('items');
},
$scope.total = function() {
var total = 0;
angular.forEach($scope.invoice.items, function(item) {
total += item.qty * item.cost;
})
return total;
}
}
HTML contains a button , which pushes the new items to the array which gets automatically binded.
<div ng:controller="CartForm">
<table class="table">
<tr>
<th>Description</th>
<th>Qty</th>
<th>Cost</th>
<th>Total</th>
<th></th>
</tr>
<tr ng:repeat="item in invoice.items">
<td><input type="text" ng:model="item.description"class="input-small"></td>
<td><input type="number" ng:model="item.qty" ng:required class="input-mini"> </td>
<td><input type="number" ng:model="item.cost" ng:required class="input-mini"> </td>
<td>{{item.qty * item.cost | currency}}</td>
<td>
[<a href ng:click="removeItem($index)">X</a>]
</td>
</tr>
<tr>
<td><a href ng:click="addItem()" class="btn btn-small">add item</a></td>
<td></td>
<td>Total:</td>
<td>{{total() | currency}}</td>
</tr>
</table>
</div>
Local stage saves only strings, not complex objects.
What you can do, therefore, is stringify it when saving and re-parse it when accessing it.
localStorage['foo'] = JSON.stringify([1, 2, 3]);
Be aware that the stringify process will strip out any unsuitable elements in the array, e.g. functions.
To re-parse it:
var arr = JSON.parse(localStorage['foo']);
localStorage["items"] = JSON.stringify(items);
update: you can retrieve it as follows: `var items:
localStorage.getItem('items');
source
localStorage supports only strings, so that why you must use JSON.stringify() and JSON.parse() to work via localStorage.
var p = [];
p[0] = "some";
localStorage["p"] = JSON.stringify(p);
For your code:
var items = [{
qty: 10,
description: 'item',
cost: 9.95}];
localStorage.setItem("items", JSON.stringify(items));
// get
var items = JSON.parse(localStorage.getItem("items"));
localStorage supports only strings, so you must use the following code:
var p = [];
p[0] = "some";
localStorage["p"] = JSON.stringify(p);

Categories