using toFixed() in Angular is not working - javascript

I am having two issue in this page
https://plnkr.co/edit/c7r4CAR1WJrOzQUybip2?p=preview
When the USD amount is 3, it gives 2.219999 value in EUR. I have tried using .toFixed(2) but it's not working.
Another problem is I am calling ng-model="curr.to()". Even though it outputs the result correctly, there is a console error.
angular.js:13920 Error: [ngModel:nonassign] Expression 'curr.to()' is non-assignable. Element: <input type="number" ng-model="curr.to()" class="ng-pristine ng-untouched ng-valid">
Any help would be appreciated.

1. You probably got an error, because toFixed converts the number to a string, and you can't bind it to your input. A simple Number(watever.toFixed()) would do.
2. You can't bind a function expression to ngModel since it performs two-way data binding. You can alternatively use watch instead.
Note: I am personally against your 1st method. The controller should not care how the data be displayed on the view. You should consider another approach by using filter, etc..
However, here's a sample of your working version.
(function(angular) {
'use strict';
angular.module('finance', []);
angular.module('finance').factory('currencyConverter', function() {
var currencies = ['USD', 'EUR', 'CNY'];
var usdToForeignRates = {
USD: 1,
EUR: 0.74,
CNY: 6.09
};
var convert = function(amount, inCurr, outCurr) {
var res = amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr]
return Number(res.toFixed(2));
};
return {
currencies: currencies,
convert: convert
}
});
angular.module('currencyConvert', ['finance']);
angular.module('currencyConvert').controller('currencyCnvtCtrl', ['$scope', 'currencyConverter',
function InvoiceController($scope, currencyConverter) {
this.from = 1;
this.inCurr = 'USD';
this.outCurr = 'CNY';
this.currencies = currencyConverter.currencies;
this.to = 0;
var w1 = $scope.$watch('curr.from', function() {
$scope.curr.to = currencyConverter.convert($scope.curr.from, $scope.curr.inCurr, $scope.curr.outCurr);
});
var w2 = $scope.$watch('curr.inCurr', function() {
$scope.curr.to = currencyConverter.convert($scope.curr.from, $scope.curr.inCurr, $scope.curr.outCurr);
});
var w3 = $scope.$watch('curr.outCurr', function() {
$scope.curr.to = currencyConverter.convert($scope.curr.from, $scope.curr.inCurr, $scope.curr.outCurr);
});
var w4 = $scope.$watch('curr.to', function() {
$scope.curr.from = currencyConverter.convert($scope.curr.to, $scope.curr.outCurr, $scope.curr.inCurr);
});
$scope.$on('$destroy', function() {
w1();
w2();
w3();
w4();
});
}
]);
})(window.angular);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="currencyConvert">
<h1>Currency Converter</h1>
<section class="currency-converter" ng-controller="currencyCnvtCtrl as curr">
<h4>Type in amount and select currency</h4>
<input type="number" ng-model="curr.from">
<select ng-model="curr.inCurr">
<option ng-repeat="c in curr.currencies">{{c}}</option>
</select>
<br>
<input type="number" ng-model="curr.to">
<select ng-model="curr.outCurr">
<option ng-repeat='c in curr.currencies'>{{c}}</option>
</select>
</section>
</div>

Related

How to bind input value to $scope object in angular js?

I'm new to Angular JS and I want to bind the input value to $scope object, just like we use ng-bind and ng-model for input fields to bind their values to DOM (the value changes as we type something in the input).
Can't I do the same? I mean just like displaying live text entered into input should be stored to $scope.foo.bar and hence it is printed in the console?
Here's what I'm trying:
<script type="text/javascript">
var app = angular.module("testApp", []);
app.service("stringService", function(){
this.myFunction = function(word){
return word.toUpperCase();
};
});
app.controller("control", function($scope, $location, stringService, $http){
$scope.age=24;
$scope.foo = {bar: "hello"};
console.log($scope.foo.bar);
$scope.isNumber = angular.isNumber($scope.foo.bar);
});
</script>
<div ng-app="testApp">
<div ng-controller="control" ng-init="obj=[{name:'vikas'}, {name: 'vijay'}, {name: 'vinay'}]; mySwitch=true">
<form name="testForm">
<input type="text" name="bar" ng-model="foo.bar" required>
</form>
<div>{{isNumber}}</div>
</div>
</div>
I can see the initial value (hello) in the console and false in DOM. But it doesn't update.
this line $scope.isNumber = angular.isNumber($scope.foo.bar); will only run once which is at angular initialize the page.
you can change isNumber to function in order to call multiple times.
$scope.isNumber = function() {
return angular.isNumber($scope.foo.bar);
}
call it at template:
<div>{{isNumber()}}</div>
var app = angular.module("testApp", []);
app.service("stringService", function() {
this.myFunction = function(word) {
return word.toUpperCase();
};
});
app.controller("control", function($scope, $location, stringService, $http) {
$scope.age = 24;
$scope.foo = {
bar: 1
};
console.log($scope.foo.bar);
$scope.isNumber = function() {
console.log(Number.isFinite($scope.foo.bar));
console.log(typeof($scope.foo.bar));
return angular.isNumber($scope.foo.bar);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<div ng-app="testApp">
<div ng-controller="control" ng-init="obj=[{name:'vikas'}, {name: 'vijay'}, {name: 'vinay'}]; mySwitch=true">
<form name="testForm">
<input type="text" name="bar" ng-model="foo.bar" required>
</form>
<div>{{isNumber()}}</div>
</div>
</div>
while #Pengyy's is quite correct in saying that you need to databind to a function, that is only part of the problem...
The problem is NaN, a perplexing numeric value that is not a number, but has type 'number' and some quicks having to do with bindings to string valued attributes like <input>'s value in html.
// the constant NaN
console.info(NaN);
// the type of the constant NaN
console.info(typeof NaN);
// NaN is not equal to NaN
console.info(NaN === NaN);
// Number.isNaN checks for NaN
console.info(Number.isNaN(NaN));
// Number.isNaN returns false for any value unless typeof value === 'number'
console.info(Number.isNaN({
a: []
}));
surprisingly, Angular's angular.isNumber function does not help us deal with these oddities. As stated in the documentation it
Determines if a reference is a Number.
This includes the "special" numbers NaN, +Infinity and -Infinity.
If you wish to exclude these then you can use the native `isFinite' method.
Ref: https://docs.angularjs.org/api/ng/function/angular.isNumber
To make matters worse, it also ignores all values for which typeof value !== 'number'
Finally, the last hurdle to overcome is that an HTML input's value is always a string! This means we need to convert it to number.
Therefore, the function needs to look like
$scope.isNumber = function(n) {
// must convert to number because `Number.isNaN` does not coerce
return isFinite(n) && !Number.isNaN(Number(n));
}
And the binding like
<div>{{isNumber(foo.bar)}}</div>
Here is an example
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script type="text/javascript">
(function() {
"use strict";
var app = angular
.module("testApp", [])
.controller({
MyController
});
MyController.$inject = ['$scope'];
function MyController($scope) {
$scope.foo = {
bar: "hello"
};
console.log($scope.foo.bar);
$scope.isNumber = function(n) {
console.info(typeof n);
return isFinite(n) && angular.isNumber(Number(n));
}
}
}());
</script>
<div ng-app="testApp">
<div ng-controller="MyController">
<form name="testForm">
<input type="type" name="bar" ng-model="foo.bar" required>
</form>
<div>{{isNumber(foo.bar)}}</div>
</div>
</div>
Notice how the raw type of the input is always 'string'
Value must be updating but you do not have anything to communicate, You need a watcher or a function that fires the console log and checks for number.
Change your controller to :-
app.controller("control", function($scope, $location, stringService, $http){
$scope.age=24;
$scope.foo = {bar: "hello"};
console.log($scope.foo.bar);
$scope.isNumber = angular.isNumber($scope.foo.bar);
$scope.check = function() {
$scope.isNumber = angular.isNumber($scope.foo.bar);
console.log($scope.foo.bar);
console.log($scope.isNumber);
}
});
And in Html
<input type="type" name="bar" ng-model="foo.bar" ng-change="check()" required>

how to write mm/dd/yyyy date format for input field in javascript?

I have to format a date in an input textbox in javascript or angular, and I am facing an issue in my code.
HTML:
<input type="text" class="form-control" name="dateOfBirth" ng-model="dob" placeholder="Birthday(MM/DD/YYYY)" ng-required="true" ng-change="change()" maxlength="10" ng-maxlength="10" dob-format>
JS:
vm.change = function (e) {
if (vm.dob) {
var dobLen = vm.dob.length;
if (dobLen === 2 || dobLen === 5)
vm.dob = vm.dob + "/";
}
};
If I type "6"(month) instead of "06", it must automatically change to "06", here I am facing an issue where the forward slash will come when I type two letters and the backspace is not working. So anyone please help me.
Any help would be appreciated.
Change accordingly in change event.
Regex_replace('0' + Replace([Starting Date], '/', '/0'), '0*(\d\d)/0*(\d\d)/0*(\d\d)', '20$3-$1-$2')
public static getDateForm_german(date: Date): string {
let filter = {
year: 'numeric',
month: '2-digit',
day: '2-digit'
};
return date.toLocaleString('de-DE', filter);
// returns like 30.02.1999 for 1999-02-30
}
i know it is not the format you are looking for, and it is typescript. but i think you are looking for something like this.
Try like below:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('appCtrl', ['$scope', function($scope){
vm=$scope;
vm.change = function (e) {
if (vm.dob) {
var dobLen = vm.dob.length;
if (dobLen === 2 || dobLen === 5)
vm.dob = vm.dob + "/";
}
};
}]);
</script>
HTML:
<div ng-app="myApp" ng-controller="appCtrl">
<input type="text" class="form-control" name="dateOfBirth" ng-model="dob" placeholder="Birthday(MM/DD/YYYY)" ng-required="true" ng-change="change()" maxlength="10" ng-maxlength="10" dob-format>
</div>
This is working like charm for me.
A mixture of the pattern attribute and some onchange formatting can fix this:
var inp = document.body.appendChild(document.createElement("input"));
inp.pattern = "\\d\\d/\\d\\d/\\d\\d";
inp.placeholder = "YY/MM/DD";
inp.required = true;
function formatDate(evt) {
var val = inp
.value
.toString().split("/")
.map(function(a) {
if (a.length == 1) {
return "0" + a;
}
return a;
})
.join("/");
if (/\d\d\/\d\d\/\d\d/.test(val)) {
inp.value = val;
}
}
inp.onchange = formatDate;

ng repeat on a array to filter a key on another object to get the associated value and use watch on that value

Let say i have a object called,
scope.rec = {a: 2, b: 3, name: a,b};
And i split the "name" key like scope.x = scope.rec.name.split(","); then scope.x will become an array.
Now i need to iterate over "scope.x" in the view and get the value associated with the matching property name on scope.rec. I only want to iterate over the valid property names, so I will need to use a filter on scope.x, but it is not working as I would expect.
Once I get the first part working, I will also need to add functionality to multiply the values of the scope.rec properties together - in the example above, it is only 2 numbers (a,b), but it could be more than 2.
Below is the code that I tried.
scope.x =
scope.rec.name.split(",");
scope.myFilter = function(y) {
if(!scope.rec.hasOwnProperty(y)) return false;
scope.ys = Number(scope.rec[y]);
return scope.ys;
};
html:
<div ng-repeat="y in x | filter:myFilter">
<label for="{{y}}">{{y}}</label>
<input type="number" id="{{y}}" value={{ys}}>
</div>
<div><input id="calc" type="number" ng-model="calc()" disabled></div>
Now the ys in the input is same for both inputs, and the calc() function does not calculate the values correctly.
Appreciate your help in advance.
your filter (at least how you use it in your view) will receive an array with all elements, not just one. So you need to return a complete array
angular.module('myApp').filter('myFilter', function() {
return function(arrayOfYs, recFromScope) {
var filtered = [];
arrayOfYs.forEach(function(y){
if(!recFromScope.hasOwnProperty(y)) return;
// if the value in the object is already a number, it is not necessary to use Number. If it is not the case, add it
filtered.push(scope.rec[y]);
});
return filtered;
}
});
and return the filtered data.
According to your view, you need to use angular filters.
for you input you should use this
<input type="number" id="{{y}}" value={{y}}>
although I would remove that id - ids needs to be unique and probably there are values repeated.
for your calc() function you can use reduce to multiply them
$scope.calc = function(){
return $scope.filteredItems.reduce(function(prev, current) {
return prev * current;
}, 1);
};
and to get a reference to $scope.filteredItems use this in your view
<div ng-repeat="y in (filteredItems = (x | filter:myFilter:rec))">
You could do something like this:
angular.module('myApp', [])
.controller('MyController', MyController);
function MyController($scope) {
$scope.result = 1;
$scope.recObj = {};
$scope.rec = {
a: 2,
b: 3,
c: 5,
name: 'a,b,c,d'
};
function initData() {
var dataKeysArr = $scope.rec.name.split(",");
for (var dataKey of dataKeysArr) {
if ($scope.rec.hasOwnProperty(dataKey)) {
$scope.recObj[dataKey] = $scope.rec[dataKey];
}
}
};
initData();
// Watch
$scope.$watchCollection(
"recObj",
function() {
$scope.result = 1;
for (var dataKey in $scope.recObj) {
$scope.result *= $scope.recObj[dataKey];
};
}
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MyController">
<div ng-repeat="(key, val) in recObj">
<label for="{{key}}">{{key}}</label>
<input type="number" id="{{key}}" ng-model="recObj[key]">
</div>
<div>
<input id="calc" type="number" ng-model="result" disabled>
</div>
</div>
</div>

How to take object input in angularjs

I have a part of form like this
<input type="text" ng-model="gen.name" ng-repeat="gen in movie.genres">
gen in movie.genre is an object with 2 key value pairs
Object{
id: 24,
name : insidious
}
If I take the values on form submit I only get gen.name as value
but I need the id of gen object as well.
So what can I do to get its id as well?
Please note there are multiple input values as they are inside ng-repeat
You can use Array.findIndex() to find the id of the object which has the name of the selected genre.
Something like this should work (as long movie.genres is an array and valueFromForm is the value you get from your ng-model):
let index = movie.genres.findIndex(gen => gen.name === valueFromForm);
id = movie.genres[index].id;
This code uses ES6 so it needs recent browsers if you do not transcompile.
You can use this other version for older browsers:
var id = -1;
for (var i=0; i < movie.genres.length; i++) {
if (movie.genres[i].name === valueFromForm) {
id = movie.genres[i].id;
break;
}
}
Here is a working example of exactly what you want. I hope this will help you fix your problem:
angular.module('submitExample', [])
.controller('ExampleController', ['$scope', '$log', function($scope, $log) {
$scope.movie = {
title: 'Movie Title',
genres: [{
name: 'Action'
}, {
name: 'Drama'
}, {
name: 'Comedy'
}]
};
$scope.submit = function() {
$log.info($scope.movie);
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.1/angular.min.js"></script>
<div ng-app="submitExample">
<form ng-submit="submit()" ng-controller="ExampleController">
<input ng-model="gen.name" ng-repeat="gen in movie.genres"><br>
<input type="submit" value="Submit">
</form>
</div>

angular: updating view when input changes + .this/$scope

I have a few questions I cant seem to figure out. I tried to make a fiddle to show my problem but I cant seem to get it to work although it is working in my app.(https://jsfiddle.net/6dfyx2og/1/#&togetherjs=KxPTHsH0Pu). Values show up on screen and can be manipulated by the input boxes. But now $scope.booking.nights wont update if a user changes the value. (In my app when a user fills out both the arrival date and departure date it calls the function calNights that gives us the number of nights).
can anyone see my error?
Since I was not able to get the fiddle working I will post code here
controller:
angular.module('squawsomeApp')
.controller('BookingCtrl', function($scope){
$scope.values = [1,2,3,4,5];
// var self = this;
function parseDate(str) {
var mdy = str.split('/')
return new Date(mdy[2], mdy[0]-1, mdy[1]);
}
function daydiff(first, second) {
return (second-first)/(1000*60*60*24);
}
var calCheckIn = new Pikaday({
field: document.getElementById('checkin'),
format: 'MM/DD/YYYY',
onSelect: function() {
$scope.booking.checkin = null;
$scope.booking.checkin = this.toString('MM/DD/YYYY');
calNights();
}
});
var calCheckOut = new Pikaday({
field: document.getElementById('checkout'),
minDate: $scope.checkin,
format: 'DD/MM/YYYY',
onSelect: function() {
$scope.booking.checkout = this.toString('MM/DD/YYYY');
calNights();
}
});
var calNights = function(){
console.log('outside')
$scope.booking.nights = null;
console.log($scope.booking.nights)
if ($scope.booking.checkin && $scope.booking.checkout) {
console.log('inside')
$scope.booking.nights = daydiff(parseDate($scope.booking.checkin), parseDate($scope.booking.checkout));
// return daydiff(parseDate(self.booking.checkin), parseDate(self.booking.checkout))
console.log($scope.booking.nights)
}
};
var calCost = function(){
if ($scope.booking.nights < 7) {
$scope.booking.cost = 145;
} else if ($scope.booking.nights >= 7 && $scope.booking.nights <= 29){
$scope.cost = 135;
} else if ($scope.booking.nights >= 29){
$scope.booking.cost = 120;
}
};
$scope.booking = {
checkin: null,
checkout: null,
guests: null,
nights: null, //daydiff(parseDate(self.booking.checkin), parseDate(self.booking.checkout))
cost: null,
pretotal: this.nights * this.cost,
tax: 0,
total: this.pretotal + this.tax
};
});
html:
<div controller="BookingCtrl">
<form name="bookingForm" ng-submit="">
<div class="form-fields">
<label for="checkin">Check In</label>
<input id="checkin" name="checkin" placeholder="dd-mm-yyyy" type="text" ng-model="booking.checkin">
<label for="checkout">Check Out</label>
<input id="checkout" name="checkout" placeholder="dd-mm-yyyy" type="text" ng-model="booking.checkout">
<label for="guests">Guests</label>
<select ng-model="booking.guests" ng-options="value for value in values"></select>
<ul>
<li>{{booking.checkin}}</li>
<li>{{booking.checkout}}</li>
<li>{{booking.nights}}</li>
<li>{{booking.guests}}</li>
<li>{{booking.cost}} x {{booking.nights}}</li>
<li>Tax</li>
<li>Total</li>
</ul>
<ul>
<li>{{booking.pretotal}}CND</li>
<li>{{tax}}CND</li>
<li>{{booking.total}}</li>
</ul>
<input type="submit" value="Submit" ng-disabled="bookingForm.$invalid">
</div>
</form>
</div>
as an aside I initially I wanted to use the this/self syntax inside the controller instead of using $scope. This worked well but I couldn't get the values to display in the view. (the ones bound to inputs would show up, but that was just due to data binding and they would not show the inital values. If some one could explain why this is please let me know.
ie why this in the controller did not show the values in the view
var self = this;
self.booking = {
checkin: null,
checkout: null,
guests: null,
nights: null,
cost: null,
pretotal: this.nights * this.cost,
tax: 0,
total: this.pretotal + this.tax
};
but this did:
$scope.booking = {
checkin: null,
checkout: null,
guests: null,
nights: null,
cost: null,
pretotal: this.nights * this.cost,
tax: 0,
total: this.pretotal + this.tax
};
The scope object ($scope) is what enables the controller and the view to communicate with each other. If you put your properties on self instead of the scope object, there is no way for the view to use them, because they are properties of the controller itself, not the scope object. When you data-bind input elements in a view, Angular looks for the bound properties in the scope object that it created and passed to the controller. If it doesn't find them, it will create properties in the scope object and bind the inputs to them. On the other hand, if you have a property of the scope object that you have given a value in your controller, then Angular will bind that property to the input that you have data-bound to the property. Therefore, the input will have an initial value on the page that is whatever your controller set it to.
You can use the this / self syntax - but you should be using ControllerAs Syntax.
<div ng-controller="BookingCtrl as book">
<p>{{ book.booking.checkin }}</p>
</div>
Read all bout it in the docs :)
https://docs.angularjs.org/api/ng/directive/ngController

Categories