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

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>

Related

Function treated as string in Angular JS

I have an html form that takes a name and a location and Posts it to a mobile service table.
<form name="userform" ng-submit="addName(user)">
<p>name: <input type="text" id="name" ng-model="user.name" /></p>
<p>location: <input type="text" id="location" ng-model="user.location"/></p>
<button id="btn-add-evangelist">Add to list</button>
</form>
and this is how I retrieve data from the form in Angular
$scope.people = [];
$scope._name = "Default Name";
$scope._location = "Default Location";
$scope.user = {
name: function (theName) {
if (angular.isDefined(theName)) {
$scope._name = theName;
}
return $scope._name;
},
location: function (theLocation) {
if (angular.isDefined(theLocation)) {
$scope._location = theLocation;
}
return $scope._location;
}};
however, when I run the html, the location textbox has the function code instead of the "Default Location" string, and the name textbox is blank instead of "Default Name".
I wonder what can be wrong here. Any help is appreciated.
AngularJS works correct. It basically takes the string representation of the function, and sets it as the value of the textbox.
If you need the evaluated value instead, you need to call the function by putting a parentheses after the function name, like this:
angular.module('myapp', [])
.controller('myctrl', function($scope) {
$scope.people = [];
$scope._name = "Default Name";
$scope._location = "Default Location";
$scope.user = {
name: function(theName) {
if (angular.isDefined(theName)) {
$scope._name = theName;
}
return $scope._name;
}(),
location: function(theLocation) {
if (angular.isDefined(theLocation)) {
$scope._location = theLocation;
}
return $scope._location;
}()
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp" ng-controller="myctrl">
<form name="userform" ng-submit="addName(user)">
<p>name: <input type="text" id="name" ng-model="user.name" /></p>
<p>location: <input type="text" id="location" ng-model="user.location" /></p>
<button id="btn-add-evangelist">Add to list</button>
</form>
</div>
You can set directly the default value to the model like this:
$scope.people = [];
$scope._name = "Default Name";
$scope._location = "Default Location";
$scope.user = { //set default value to the inputs
name:$scope._name,
location:$scope._location
}
If you are using latest version of angular js. Try ng-model-options="{ getterSetter: true }".
Sometimes it's helpful to bind ngModel to a getter/setter function. A
getter/setter is a function that returns a representation of the model
when called with zero arguments, and sets the internal state of a
model when called with an argument. It's sometimes useful to use this
for models that have an internal representation that's different from
what the model exposes to the view.
Best Practice: It's best to keep getters fast because AngularJS is
likely to call them more frequently than other parts of your code. You
use this behavior by adding ng-model-options="{ getterSetter: true }"
to an element that has ng-model attached to it. You can also add
ng-model-options="{ getterSetter: true }" to a , which will
enable this behavior for all s within it. See ngModelOptions
for more.
angular.module('myapp', [])
.controller('myctrl', function($scope) {
$scope.people = [];
$scope._name = "Default Name";
$scope._location = "Default Location";
$scope.user = {
name: function(theName) {
if (angular.isDefined(theName)) {
$scope._name = theName;
}
return $scope._name;
},
location: function(theLocation) {
if (angular.isDefined(theLocation)) {
$scope._location = theLocation;
}
return $scope._location;
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.min.js"></script>
<div ng-app="myapp" ng-controller="myctrl">
<form name="userform" ng-submit="addName(user)">
<p>name: <input type="text" id="name" ng-model="user.name" ng-model-options="{ getterSetter: true }"/></p>
<p>location: <input type="text" id="location" ng-model="user.location" ng-model-options="{ getterSetter: true }"/></p>
<button id="btn-add-evangelist">Add to list</button>
</form>
</div>

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>

using toFixed() in Angular is not working

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>

Validating custom component in angular 1.5

I was working in angular project, There I had come across a situation in which I need to validate custom component having a textbox.
<dollar-text-validate ng-model="ctrl.value" required name="myDir"></dollar-text-validate>
My Component
angular.module("myApp", []);
angular.module("myApp").controller('MyController',function(){
var ctrl = this;
ctrl.value = 56;
});
angular.module("myApp").component('dollarTextValidate',{
bindings: {
ngModel :'='
},
template: '<div><input type="text" ng-focus="ctrl.focus()" ng-blur="ctrl.blur()" ng-model="ctrl.amount1"><input type="hidden" ng-model="ctrl.ngModel"></div>',
controller: function() {
var ctrl = this;
// ctrl.amount1 =
ctrl.amount1 =ctrl.ngModel===undefined||ctrl.ngModel==='' ? '' :'$'+ctrl.ngModel;
console.log(ctrl.ngModel);
ctrl.focus=function(){
ctrl.amount1 = ctrl.amount1 === undefined ? '' : ctrl.amount1.slice(1);
ctrl.ngModel = ctrl.amount1;
console.log(ctrl.ngModel);
}
ctrl.blur=function(){
ctrl.ngModel = ctrl.amount1;
ctrl.amount1 = ctrl.amount1==='' ? '' :'$'+ctrl.ngModel;
console.log(ctrl.ngModel);
}
},
controllerAs:'ctrl'
})
This component is used to set $ symbol in front of entered value. So $ appended value will be available in textbox and original value which is to be validated in hidden field.
How can I validate hidden field. I tried with required attribute in hidden tag but nothing happening. Also tried with custom tag.
Sorry to break it to you, but you might wanna go for directive, and then use $parsers, $formatter and $validators properties of ngModelController.
Component can be used for this, but it is just easier with normal directive.
angular.module('myApp', []);
angular.module("myApp").directive('dollarTextValidate', function() {
return {
require: 'ngModel',
link: function($scope, $element) {
var regexp = /^\$(\d+(\.\d+)?)$/;
var ngModel = $element.controller('ngModel');
ngModel.$formatters.push(function(value) {
return value ? '$' + value : '';
});
ngModel.$parsers.push(function(value) {
var matched = value.match(regexp);
if (matched) {
return +matched[1];
}
});
ngModel.$validators.greaterThan10 = function (modelVal, viewVal) {
var value = modelVal || viewVal;
return value > 10;
};
},
controllerAs: 'ctrl'
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
<div ng-app="myApp" ng-form="form">
dollar-text-validate = <input dollar-text-validate ng-model="value" required name="myDir"><br>
number input = <input type="number" ng-model="value" required><br>
value = {{value}}<br>
errors = {{form.myDir.$error}}
</div>

using $watch in angularjs

I have a form with 2 input fields and requirement is that once user enters valid data into these
fields, I need to pass the input data to the factory function and get the data from server.To achieve this I thought of using $watch function but stuck at how to know if form is valid in $wathc function and then call the factory function to get data from the server.Here is the code.
Any help would be highly appreciated.
//html
<html>
<body ng-app="myModule">
<div ng-controller="myCtrl">
Product Id: <input type="text" ng-model="myModel.id" /><br/>
Product Name: <input type="text" ng-model="myModel.productname" /><br/>
</div>
</body>
</html>
//js
var myModule = angular.module('myModule',[]);
myModule.controller('myCtrl',['$scope', function($scope){
$scope.myModel = {};
var getdata = function(newVal, oldVal) {
};
$scope.$watch('myModel.id', getdata)
$scope.$watch('myModel.productname', getdata)
}]);
Wouldn't you just watch myModel, since the same function is called in both cases?
You could do this with ng-change just as easily.
<html>
<body ng-app="myModule">
<form name="productForm" ng-controller="myCtrl">
<div>
Product Id: <input type="text" name="idModel" ng-model="myModel.id" ng-change="validateID()" /><br/>
Product Name: <input type="text" ng-model="myModel.productname" ng-change="validateProduct()" /><br/>
</div>
</form>
</body>
And your JS would look like this:
var myModule = angular.module('myModule',[]);
myModule.controller('myCtrl',['$scope', 'myFactory',
function($scope, myFactory){
$scope.myModel = {};
$scope.validateID = function(){
//things that validate the data go here
if(your data is valid){
myFactory.get(yourParams).then(function(data){
//whatever you need to do with the data goes here
});
}
};
$scope.validateProduct = function(){
//things that validate the data go here
if(your data is valid){
myFactory.get(yourParams).then(function(data){
//whatever you need to do with the data goes here
});
}
};
}
]);
Using ng-change saves you from having to add a $watch to your scope (they are expensive) and will fire when the user leaves the input box. If you need to catch each keystroke, I would recommend that you use UI-Keypress and run the same functions.
To know if form is valid you have to add a form tag and inside your controller check $valid, on your example the form is always valid becaus you do not have any required field.
See the below example on codepen
The HTML
<div ng-app="myModule">
<div ng-controller="myCtrl">
<form name="myform" novalidate>
Product Id:
<input type="text" ng-model="myModel.id" />
<br/>
Product Name:
<input type="text" ng-model="myModel.productname" />
<br/>
</form>
<br/>
<div>{{result}}</div>
<div>Form is valid: {{myform.$valid}}</div>
</div>
</div>
The JS
var myModule = angular.module('myModule', []);
myModule.controller('myCtrl', ['$scope', function ($scope) {
$scope.myModel = {};
$scope.result = "(null)";
var getdata = function (newVal, oldVal) {
var valid = null;
if ($scope.myform.$valid) {
valid = "is valid";
} else {
valid = "is INVALID";
}
$scope.result = "Changed value " + newVal + " form " + valid;
};
$scope.$watch('myModel.id', getdata);
$scope.$watch('myModel.productname', getdata);
}]);

Categories