Angularjs ng-model with number input - angularjs

I'm trying to retrieve an input[number] in my controller.
I found a solution which said to add a $scope.$watch on the input.
I tried it but now my number is "NaN".
<div class="offerText">
<label class="form-inline" ng-if="!admin">What will it cost ? <input type="number" class="form-control" ng-model="estimation"/></label>
<label class="form-inline">Comments <textarea rows="3" cols="50" class="form-control" ng-model="comments"></textarea></label>
</div>
And In my controller
$scope.$watch('estimation', function (val, old) {
$scope.estimation = parseFloat(val);
});
Thx for your help !

You don't need a watch. The problem here is that the input is inside an ng-if directive, which defines its own scope. The estimation is stored in the ng-if scope instead of being stored in the controller scope.
Rule or thumb: always have a dot in your ng-model.
In your controller, add
$scope.data = {};
And in the html, use
ng-model="data.estimation"
and
ng-model="data.comments"

Test if the new value is not undefined or null or something falsy, this happens because angular loops a few times through the watchs and the models, this process is call dirty checking
$scope.$watch('estimation', function (val, old) {
if (!val) {
return;
}
$scope.estimation = parseFloat(val);
});

When your value is "" or null you will always get parseFloat result as NaN
$scope.$watch('estimation', function (val, old) {
return !isNaN(val) ? parseFloat(val): 0; //considering default value is 0
});
Other possible way would get value on change, using ng-change directive.
<div class="offerText">
<label class="form-inline" ng-if="!admin">What will it cost ? <input type="number" class="form-control" ng-model="estimation" ng-change="changedEstimation(estimation)"/></label>
<label class="form-inline">Comments <textarea rows="3" cols="50" class="form-control" ng-model="comments"></textarea></label>
</div>
Controller
$scope.changedEstimation = function(val){
return !isNaN(val) ? parseFloat(val): 0;
}

Related

Calculate percentage based on input fields in Angularjs

I am using AngularJS. I have two input fields: total marks and marks obtained, as well as a 3rd field which is percentage. Now after entering total marks and marks obtained, I need to calculate the percentage automatically.
Here is my html :
<div class="form-group">
<label class="control-label">Total Marks</label>
<input type="text"
class="form-control input_field"
name="totalMarks"
data-ng-model="student.totalMarks" />
</div>
<div class="form-group">
<label class="control-label">Marks Obtained</label>
<input type="text"
class="form-control input_field"
name="marksObtained"
data-ng-model="student.marksObtained" />
</div>
<div class="form-group">
<label class="control-label">Percentage</label>
<input type="text"
class="form-control input_field"
value={{ ((student.marksObtained/student.totalMarks)*100).toFixed(2) }}
name="rank"
data-ng-model="student.rank" />
</div>
Can anyone tell me how to auto generate rank field with percentage as soon as I enter marks obtained. Currently, I am doing as shown above but it is not working.
Not sure that your calcul for percentage is exact, but this Plunkr is working with a watcher on student properties.
app.controller('MainCtrl', function($scope){
$scope.$watchGroup(['student.totalMarks', 'student.marksObtained'], function(){
if($scope.student.marksObtained !== undefined && $scope.student.totalMarks !== undefined)
$scope.student.rank = (($scope.student.marksObtained/$scope.student.totalMarks)*100).toFixed(2);
})
});
https://plnkr.co/edit/NsNge0mzrgjjUgBZ332p?p=preview
You can try assigning your calculation inline, like this:
<input ng-model="student.rank" ng-value="student.rank = ((student.marksObtained/student.totalMarks)*100).toFixed(2)">
Running Example on JsFiddle
It does not work because your input is linked to a ng-model.
There is two ways to solve this problem :
Setup a watcher on student.totalMars and student.marksObtained, and update student.rank with new value.
Do not link your percentage to student.rank and just set the value as you did.
You can check here for the second way.
In your controller, add the following function and variable :
function getPercentage() {
// dividing by zero is bad !!!
let ratio = $scope.student.marksObtained / ($scope.student.totalMarks || 1);
return (ratio * 100).toFixed(2);
}
$scope.student.percentage = getPercentage();
Then change the last <input /> of your view to :
<input type="text"
class="form-control input_field"
ng-value="student.percentage"
name="rank" />
If you want your input to have two-way data binding, use ng-model instead of ng-value.

Angular best way to watch two form fields

I have an array of persons, each one should have phone and name,
so I did :
<div ng-repeat="a in arr">
<ng-form>
phone{{a}}:<input type="phone" name="phone" />
text{{a}}:<input type="text" name="name" />
</ng-form>
</div>
When finish to enter phone and name for the first person, I want to call "doSomething" function .
What is the best way to do that? I prefer to not use watch.
Here is a plunker for example.
I'd suggest you to use ng-blur in your case. Using ng-keyup will trigger the function on every key release event, which is not good at all. You should check below example example how it works
phone{{a}}:<input type="phone" data-ng-blur="doSomething();" name="phone" />
I've updated your fiddle to check more.
EDIT
If you only want to execute for the first iteration then you should pass index to your function and check if its equals to 0. Then it must be for first iteration. like
ng-blur="doSomething($index)"
In your function
$scope.doSomething = function(index){
if(index === 0)
alert("finish");
};
one of the ways to achieve this is
<input type="phone" name="phone" ng-model="input.phone" ng-change="onchange()"/>
<input type="text" name="name" ng-model="input.name" ng-change="onchange()"//>
and then on your scope
scope.input = {};
scope.onchange = function(){
if(!input.phone) return;
if(!input.name) return;
//other logic
}
btw, best practises says always have a dot in your ng-model
Probably, you're looking for the following.
HTML
<div ng-repeat="a in arr">
<ng-form>
phone{{a}}:<input type="phone" name="phone" ng-change="doSomething()" />
text{{a}}:<input type="text" name="name" ng-change="doSomething()"/>
</ng-form>
</div>
Controller:
$scope.doSomething= function () {
// do something
};
Note: You can also use ng-blur that will be triggered only once you leave focus from input field.

angularjs one-way-data-binding, can angular do it?

I have a simple question about angularjs one-way-data-binding.
Assume that in same page, we have two input box A and B,
How can they work like:
input A will change input B, but input B will NOT change input A,
I know angular has bindonce, but I want is one-way-data-binding
thanks for your answer..... I tried, but all solutions are failed.........:(
Can we add something like directive to controll it?
You can use ng-value. It will show the model, but not update it. Doesn't require any extra JS wiring.
<input type="text" ng-model="a">
<input type="text" ng-value="a">
DEMO
DEMO
HTML
<input type="text" ng-model="a">
<input type="text" ng-model="b">
JS
// Put this code in your controller
$scope.$watch('a', function(newValue, oldValue) {
if ($scope.b === undefined || newValue !== oldValue) {
$scope.b = newValue;
}
});
Try this:
Html:
<input type="text" ng-model="a" data-ng-change="valueChange(a)">
<input type="text" ng-model="b">
Js:
$scope.valueChange=function(newValue){
$scope.b = newValue;
}

How to add object properties from ng-checked items to my ng-model object

I have four input fields which I'm using to add properties to a single object using ng-model= model.propertyName. I have a series of check boxes that I'm creating with ng-repeat that I could not figure out how to add unique propertyNames for each ng-model as they were created with the ng-repeat. As a work-around(or maybe this is correct, I'm not sure) I was able to write a function to add the checked items to an array. I was then trying to use a for-loop to iterate over the array and add each selected propertyName(string) to the ng-model object as a new property using a ng-click to call the function.
As-is when I click the "Add Technician" button I get the following error output:
TypeError: Cannot read property 'selection' of undefined
at Scope.$scope.addTechnician (..../scripts/controllers.js:
This occurs because $scope is undefined inside the conditional of my for loop in the addTechnician function. I can't understand why because when I pass $scope to the addTechnician function it recognizes the newTech inside the for loop. When I don't pass $scope to the addTechnician function it says newTech is undefined with the following error:
TypeError: Cannot set property 'cert1' of undefined
at Scope.$scope.addTechnician(.../scripts/controllers)
I'm pretty sure this has something to do with the way ng-repeat creates a new scope, which prototypically inherits from the parent scope. But again, I'm not sure.
Here is my controller
use strict';
angular.module('Carrepair2.controllers', [])
.controller('SetupCtrl', function($scope) {
$scope.certifications = [
{'name':'Engine Repair'},
{'name':'A/T & Transaxle'},
{'name':'Manual Drive Train & Axles'},
{'name':'Suspension & Steering'},
{'name':'Brakes'},
{'name':'Electrical & Electronic Systems'},
{'name':'Heating & Air Conditioning'},
{'name':'Engine Performance'},
{'name':'Light Vehicle Diesel Engines'}
];
// selected certifications
$scope.selection = [];
$scope.toggleCert = function(name) {
var idx = $scope.selection.indexOf(name);
//is currently selected
if (idx > -1) {
$scope.selection.splice(idx, 1);
}
//is newly selected
else{
$scope.selection.push(name);
}
};
$scope.addTechnician = function($scope) {
for(var i=0; i < $scope.selection.length - 1; i++){
$scope.newTech['cert' + (i + 1).toString()] = $scope.selection[i].name;
}
};
})
Here is my template
<form class="col-md-6">
<div class="input_wrapper">
<input type="text" name="first-name" ng-model="newTech.firstName" required>
<label for="first-name">First Name</label>
</div>
<div class="input_wrapper">
<input type="text" name="last-name" ng-model="newTech.lastName" required>
<label for="last-name">Last Name</label>
</div>
<div class="input_wrapper">
<input type="email" name="email" ng-model="newTech.email" required>
<label for="email">Email</label>
</div>
<div class="input_wrapper">
<input type="tel" name="phone" ng-model="newTech.phone" required>
<label for="phone">Phone</label>
</div>
<h5>Check all held ASE certifications</h5>
<ul class="list">
<li class="item item-checkbox" ng-repeat="certification in certifications">
<label class="checkbox">
<input type="checkbox" value="{{certification.name}}" ng-checked="selection.indexOf(certification.name) > -1" ng-click="toggleCert(certification)">
</label>
{{certification.name}}
</li>
</ul>
<button class="button button-block" ng-click="addTechnician()">Add Technician</button>
</form>
Ideally after the "Add Technician" button is clicked I want to end up with an object, my newTech ng-model object, that has the input field data and the properties from the checked items. Here is a jsFiddle with simplified code replicating the problem
http://jsfiddle.net/aq93z/7/
I solved it. I had to format my loop using angular.forEach(values, function(value, index){here is the jsFiddle with the solution http://jsfiddle.net/aq93z/9/. If you look at the $scope.newTech object created in the console the checkbox selections are added as properties of the newTech ng-model object.

AngularJS: ngModel with ngSwitch, cannot watch?

I'm trying to make a very simple signup form, which is supposed to work in 2 steps.
I ask the user for his email in the first step
in the second step, I ask the user for the other details (step == 2).
The template is simple;
<div class="input-group layout-container" ng-switch on="step">
<!-- Login -->
<img src="images/yeoman.png">
<p>{{msg}}</p>
<form name="signup">
<input type="text" class="form-control login" placeholder="firstname" ng-model="firstname" ng-switch-when="2">
<input type="text" class="form-control login" placeholder="lastname"ng-model="lastname" ng-switch-when="2">
<input type="email" class="form-control login" placeholder="email" ng-model="email" ng-switch-when="1" required>
<span class="error" ng-show="signup.input.$error.required">Cannot be blank</span>
<span class="error" ng-show="signup.input.$error.email">Not a valid email</span>
<input type="password" class="form-control login" placeholder="password" ng-model="pass" ng-switch-when="2"> <br/><br/>
<div class="btn-container">
<button class="btn btn-primary" ng-click="next()">Next</button>
</div>
</form>
</div>
As you can see, there's some basic validation going on as well, and I assign the value of the email to a model I call email.
And the controller goes like this:
.controller('SignupCtrl', function ($scope) {
$scope.msg = "First, your email";
$scope.step = 1;
$scope.email = ''; // this still doesn't help
$scope.$watch('email', function (now, then, scope) {
console.log('email change', now, then);
});
$scope.next = function () {
$scope.step = Math.min($scope.step + 1, 2);
};
})
The problem is this: the $watch on 'email' doesn't ever trigger at all. Where's this going wrong?
I agree with #miqid, that ng-switch creates a new scope implicitly. All the value changes of <input> are actually happened in this isolated child scope. Its parent, where you are doing $watch, will NOT be notified. That's the reason why you cannot watch the variable changes. This is a common pitfall to angular users.
LT;DR simply change any ng-model="foo" to ng-model="$parent.foo" in your ng-switch block.
Here is a working jsfiddle (of just the part you have a problem with): demo
function ctrl($scope) {
$scope.items = ['one','two'];
$scope.selection = 'two';
$scope.data = {email:''};
$scope.$watch('data.email', function (now, then, scope) {
console.log('email change', now, then);
});
}
Note that it will only trigger the watch if the email is valid if the input is type="email".
Edit: updated for use with ng-switch.
Explanation: As noted by miqid, when you use primitives, child scopes have no access to parent scope variables, but when they're objects angular creates a reference in the child scopes.

Resources