Clearing all inputs and restoring .ng-pristine on click - angularjs

It's still the first day of me using AngularJS after inheriting the project from a fellow developer. I was wondering, I have a login/registration form on my interface that is hidden once the items/form is submitted. Now once the user has submitted is there a correct or proper way to clear the form of it's entries and restore the .ng-pristine class on the items. The reason for this is should the user choose to log out and then login again (or another user wishes to register) the form is populated and has the validation css applied to it already. I don't want this, I would want everything to be empty and no CSS applied.
I can do this in JavaScript (obviously) however with AngularJS being a different approach I was wondering if I should approach this issue another way rather than loop through the form items and append a class to each item whilst clearing it's value.
This an example of one of my forms
<form name="signupForm" novalidate ng-submit="register(user)">
<div><label for="email">email:</label><input type="email" id="email" name="email" required ng-model="user.email" ng-minlength=4></div>
<div><label for="userName">Username:</label><input type="text" name="userName" id="userName" ng-model="user.userName" required ng-pattern="/^[A-Za-z0-9 \-_.]*$/" ng-minlength=4></div>
<div><label for="firstName">Vorname:</label><input type="text" name="firstName" id="firstName" ng-model="user.firstName" required ng-pattern="/^[A-Za-z \-_.]*$/" ng-minlength=3></div>
<div><label for="lastName">Nachname:</label><input type="text" name="lastName" id="lastName" ng-model="user.lastName" required ng-pattern="/^[A-Za-z \-_.]*$/" ng-minlength=4></div>
<div><label for="password1">Passwort:</label><input type="password" name="password1" id="password1" ng-model="user.password1" required ng-minlength=4></div>
<div><label for="password2">Passwort wiederholen:</label><input type="password" name="password2" id="password2" ng-model="user.password2" valid-password2 ng-minlength=4 pw-check="password1"></div>
... and so on
Many thanks

The form will appear in the correct scope under its name, i.e. $scope.signupForm. Additionally the object populating the form is $scope.user. In your controller, do:
$scope.user = {}; // or new User() if it is your case
$scope.signupForm.$setPristine();
In case $scope.signupForm is undefined, put a controller directly on the form, and place the code above (and anything else applicable) inside this new controller:
<form name="signupForm" novalidate ng-submit="register(user)"
ng-controller="NewCtrl">
(This may happen due to scope nesting under your original controller.)

Just refer to this post :
Reset form to pristine state (AngularJS 1.0.x)
In the main question you got reference to issues and pull request on AngularJS. In resume you have to use $setPristine() method to your form.
Hope it helps !

var app = angular.module('App', []);
app.controller('formController', function($scope, $document){
$scope.Clear = function(){
angular.forEach(angular.element($document[0].querySelectorAll('input[type=text], input[type=email], input[type=password]')), function (elem, index) {
elem.value = '';
/*
Just extra do something
elem.parent().parent().removeClass('has-error has-success');
elem.parent().parent().children().find('span').removeClass('glyphicon-exclamation-sign glyphicon-ok');
*/
});
$scope.myForm.$setPristine();
}
});
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js"></script>
</head>
<body ng-app="App">
<div ng-controller="formController">
<form name="myForm">
<input type="text" name="FirstName" ng-model="FN"/> <br>
<input type="text" name="LastName"/>
<br>
<input type="text" name="UserName"/>
<br>
<input type="password" name="Password"/>
</form>
<button ng-click="Clear()">Clear</button>
</div>
</body>
</html>
Here is my Example, it worked for me i am using angularjs 1.6

Related

angular validation - ng-dirty and required is not showing

i have a small angular validation where i want an error to show if a textfield is dirty and another error if it is required.
my html:
<form name="someform1" controller="validateCtrl" novalidate>
<input ng-model="namefld" type="text" required/>
<span ng-show="someform1.namefld.$dirty">pls enter name field</span>
<span ng-show="someform1.namefld.$error.required">Username is required.</span>
</form>
i have set the controller like this:
var myapp = angular.module("myApp",[]);
app.controller('validateCtrl', function($scope) {
$scope.namefld = 'John Doe';
$scope.email = 'john.doe#gmail.com';
});
"myApp" is defined in the <html> tag so that is not the problem. I am missing something and am new to angular, pls guide what i am doing wrong.
You need to add a name to the input too. As you have it set up now $dirty will only work on the form itself not on each individual input, you need to add a name to the inputs for that
Working Demo
You are missing name='namefld'
<input ng-model="namefld" name='namefld' type="text" required/>
Angular form validation works based on the name of the form and the form inputs. In your case you have specified the name of the form but not the input element. Add the name="namefld" to the input element and it will work.
<form name="someform1" novalidate>
<input ng-model="namefld" name="namefld" type="text" required/>
<span ng-show="someform1.namefld.$dirty">pls enter name field</span>
<span ng-show="someform1.namefld.$error.required">Username is required.</span>
</form>
See a working JSbin for same, that I have created

AngularJS - how to sync result of calculated input field to a scope variable

I'm trying to sync the result of a calculated form field into a scope variable. For example:
<div class="form-group">
<label for="val1" class="control-label">Val 1</label>
<input class="form-control" name="val1" type="number" ng-model="data.val1" id="val1">
</div>
<div class="form-group">
<label for="val2" class="control-label">Val 2</label>
<input class="form-control" name="val2" type="number" ng-model="data.val2" id="val2">
</div>
<div class="form-group">
<label for="score" class="control-label">Score</label>
<input class="form-control" name="score" value="{{data.val1+data.val2}}" type="text" id="score">
</div>
<br/>data: {{data}}
How can I sync the result (i.e. the score field) into the scope variable $scope.data.score? I have tried ng-model="data.score" but that breaks the calculation.
You can see the example in action here:
http://plnkr.co/edit/fc9XcyyYGtAk0aGVV35t?p=preview
How do I get the last line to read data: {"val1":1,"val2":2,"score":3}?
Note that I'm looking for a solution that involves minimal to no code support at the controller level. For example, I know you can set up a watch in the controller for both val1 and val2 and then update the score in the watcher. This is what I wanted to avoid, if it's possible in angular at all. If it's not (theoretically) possible, I'd really appreciate an explanation of why it's not.
A quick background might help. Basically we have a simple form builder app that defines a form and all its fields in an xml file. Here's a sample of what the xml would look like:
<form name="test">
<field name="val1" control="textbox" datatype="number">
<label>Val 1</label>
</field>
<field name="val2" control="textbox" datatype="number">
<label>Val 2</label>
</field>
<field name="score" control="textbox" datatype="number">
<label>Score</label>
<calculate>[val1]+[val2]</calculate>
</field>
</form>
When a form is requested, the system will need to pick up the xml, loop through all the fields and generate an angular style html to be served to the browser and processed by angular. Ideally, I want to keep all the form specific logic (validation, show/hide, calculation etc) confined to the html, and keep the controller (js) logic generic for all forms.
The only solution I can come up with is to dynamically load watcher functions, through something like this: eval("$scope.$watch('[data.val1,data.val2]')..."), but as I said, I really want to avoid this, because it's just tedious, and feels extremely dodgy :)
The first dirty way.
In your case you can move all logic from controller into html with using combination of ng-init and ng-change directives.
<div class="form-group">
<label for="val1" class="control-label">Val 1</label>
<input class="form-control" name="val1" type="number" ng-model="data.val1" ng-change="data.score = data.val1 + data.val2" id="val1">
</div>
<div class="form-group">
<label for="val2" class="control-label">Val 2</label>
<input class="form-control" name="val2" type="number" ng-model="data.val2" ng-change="data.score = data.val1 + data.val2" id="val2">
</div>
<div class="form-group" ng-init="data.score = data.val1 + data.val2">
<label for="score" class="control-label">Score</label>
<input class="form-control" name="score" ng-model="data.score" type="text" id="score">
</div>
<br/>data: {{data}}
I don't think that it's the clearest solution, but you can leave your controller without any changes with it.
Demo on plunker.
The second dirty way.
There is one more way, but now you don't need ng-init and ng-change directives. You can add just one dirty expression in html:
<div id="main-container" class="container" style="width:100%" ng-controller="MainController">
{{data.score = data.val1 + data.val2;""}} <!-- key point -->
<div class="form-group">
<label for="val1" class="control-label">Val 1</label>
<input class="form-control" name="val1" type="number" ng-model="data.val1" id="val1">
</div>
<div class="form-group">
<label for="val2" class="control-label">Val 2</label>
<input class="form-control" name="val2" type="number" ng-model="data.val2" id="val2">
</div>
<div class="form-group">
<label for="score" class="control-label">Score</label>
<input class="form-control" name="score" ng-model="data.score" type="text" id="score">
</div>
<br/>data: {{data}}
;"" in expression stops evaluating of angular expression to text in html.
Demo on plunker.
See if this works, in your HTML change,
<input class="form-control" name="score" ng-model = "data.score" type="text" id="score">
and, in your controller do,
var myApp = angular.module('myapp', [])
.controller('MainController', function($scope) {
$scope.data = { val1: 1, val2: 2, score: 3};
$scope.$watch('[data.val1,data.val2]', function (newValue, oldValue) {
$scope.data.score = newValue[0] + newValue[1];
}, true);
})
Demo plunk, http://plnkr.co/edit/gS0UenjydgId4H5HwSjL?p=preview
If you want to know how you can do it, then i have one solution for you make a ng-change event for both of your text box and sum both the number there and use ng-model in the third text box then, you can see it will work as per your need.
For the first time load you need to calculate it out side only.

AngularJS form validation testing

Sometimes forms become very complicated and it is impossible to test every case manually after code changes.
I already have unit testing with karma on the project.
Is there any tools or best practices how to test AngularJS form validation with jasmine and karma?
For example how can I test such form with jasmine and karma automatically?
<form name="appForm" novalidate>
<div>
Username: <input type="text" ng-model="data.username" name="username" ng-maxlength="15" required />
</div>
<div>
Email: <input type="email" ng-model="data.email" name="email" required />
</div>
<div>
Age: <input type="number" ng-model="data.age" name="age" />
</div>
<div>
<button ng-click="submit()" ng-disabled="appForm.$invalid">Submit</button>
</div>
</form>
It depends on what you actually want to make sure when testing form validation.
If you want to be sure invalid form will not be submited, then it is one case. And I don't see problems with this case.
If you want to be sure that appropriate messages are displayed for invalid fields, then, for example, you can make a directive, that is aware of all your possible field restrictions ('required', 'ng-maxlength', 'url', etc.) and is responsible for displaying appropriate error messages. So you will need to create tests only for this directive.
Example:
<input type="text" ng-model="data.username" my-directive name="username" ng-maxlength="15" required />
myDirective is aware of required and ng-maxlength restrictions, that were put on the field, & it is responsible for displaying appropriate error messages for invalid state of the field.

Have AngularJs update {{binding}} as the user types in input[email]

Angular only updates the model from an input[email] after the user has entered a valid email address. How can I add a {{binding}} somewhere on the page that will update with the email value as the user types -- even before the user has typed in a valid email address?
Here's what I've tried so far:
<div ng-app>
<div ng-controller="MyCtrl">
<form name="MyForm" novalidate>
Name: <input type="text" name="name" ng-model="contact.name" /><br/>
Name as you type: {{contact.name}}<br/>
Email: <input type="email" name="email" ng-model="contact.email" /><br/>
Email as you type: {{contact.email}} (doesn't work)<br/>
Also doesn't work: {{$document.forms.MyForm.elements.email.value}}
</form>
</div>
</div>
Controller:
function MyCtrl($scope) {
$scope.contact = {};
}
(fiddle)
The name updates in real-time like I want, but the email doesn't.
I'd like to leave the email validation enabled. I just need some way to bind the un-validated input[email] text, so it updates as the user types.
Update 2014/7/8
I'd like to add an explicit requirement that the type="email" remains unchanged. I do not want to change the semantics of the markup to workaround a limitation of the framework. If need be, I'd rather pull in a complementary dependency (such as jQuery) to shim in the needed functionality.
I'm not opposed to handling validation in the controller — as suggested by rageandqq and charlietfl — if it could be done easily. Looking around though, it looks like it could be tricky (given my requirements).
That is how angularjs works. If you use <input type="email" /> angular is not going to bind your input till input will be valid in this case value must be a proper e-mail address.
please read more here : https://github.com/angular/angular.js/issues/1426
The workaround I've come up with so far is to use jQuery to listen for the input change and update an object on $scope that I've called formRaw. It works. Still, I'm hoping someone will come along and show me a better way.
The updated example:
<div ng-app>
<div ng-controller="MyCtrl">
<form name="MyForm" novalidate>
Name: <input type="text" name="name" ng-model="contact.name" /><br/>
Name as you type: {{contact.name}}<br/>
Email: <input type="email" name="email" ng-model="contact.email" /><br/>
Email Model: {{contact.email}}<br/>
Email Form: {{formRaw.email}}
{{q}}
</form>
</div>
</div>
And controller:
function MyCtrl($scope) {
$scope.contact = {};
$scope.formRaw = {};
$('input[type=email]').on('keyup change', function () {
var input = $(this);
$scope.formRaw[input.attr('name')] = input.val();
$scope.$digest(); // FIXME: there's got to be a better way
});
}
(fiddle)
The type="email" attribute on your E-mail input is what is causing the DOM binding to mess up.
Changing it to type="text" works allows your {{contact.email}} to display correctly.
Edited JSFiddle.

Is ng-model needed when using ng-disabled and $invalid on a form?

I'm using AngularJS and have a form where I want the Submit button to be disabled if some fields are not filled in.
The standard way seems to be the following:
<form ng-app name="myForm">
<label>Name</label>
<input type="text" name="name" ng-model="form.name" required>
<input type="submit" ng-disabled="myForm.name.$invalid">
</form>
http://jsfiddle.net/YMSRU/
However, if I omit the model from the input field the validation doesn't work and I don't need any models on my input fields (I submit my form using the ngUpload directive so it's actually sent to the form action in an iframe).
Is there any solution or should I add random models just to make the validation work? It seems like a bad work-around.
You could simply do the invalid check at the form level, then no need to define a model for each input:
<form ng-app name="myForm">
<label>Name</label>
<input type="text" name="name" required>
<input type="submit" ng-disabled="myForm.$invalid">
</form>
You are missing your model at your test input tag : ng-model="form.name"

Resources