Angular - Cannot read property '$invalid' of undefined - angularjs

I am creating a login page using angular and typescript. When the submit button is clicked, I would like the login function in the controller to fire, but if the form is invalid then it just returns.
This is my first time using typescript, so every time I try to put in the if statement to check if the form is invalid it throws the error Cannot read property '$invalid' of undefined.
Here is the html:
<form class="login-form" name="loginForm" ng-submit="vm.login()" novalidate>
<input type="email" name="email" placeholder="Email" required ng-model="vm.email" ng-class="{true: 'input-error'}[submitted && loginForm.email.$invalid]"/>
<input type="password" name="password" placeholder="Password" required ng-model="vm.password" ng-class="{true: 'input-error'}[submitted && loginForm.password.$invalid]"/>
<input type="submit" id="submit" ng-click="submitted=true"/>
</form>
And here is the compiled javascript:
var LoginModule;
(function (LoginModule) {
var LoginController = (function () {
function LoginController() {
}
LoginController.prototype.login = function () {
if(this.loginForm.$invalid) {
return;
}
console.log("Login was clicked, email is " + this.email + " and password is " + this.password);
};
return LoginController;
})();
LoginModule.LoginController = LoginController;
})(LoginModule || (LoginModule = {}));
angular.module('loginModule', []).controller('LoginController', LoginModule.LoginController);
It looks like the common issue with this was that people were not specifying the form name, but that is not the case here. Does anyone know why I could be getting this error?

You need the controller alias in the form name
<form name="vm.loginForm" ng-submit="vm.login()" novalidate>

While #charlietfl's answer is correct for the OP's question, this error can also be thrown if you've missed the ng-model property on an input control and are using the ui.bootstrap.showErrors $scope.$broadcast('show-errors-check-validity') method.
As per the documentation (my emphasis):
.. an input control that has the ngModel directive holds an instance of NgModelController. Such a control instance can be published as a property of the form instance using the name attribute on the input control. The name attribute specifies the name of the property on the form instance.

Related

AngularJS - perform extra validation on form submit

I've been googling for a while and can't seem to find a good answer for my specific case. I've found ways to do real-time validations but I want to combine that with some custom validations after a user clicks on "submit". I want to allow the user to still click on the submit even if it's not valid but then I'll cancel the submission in the code. Take the following code:
<form name="cashForm" id="cashForm" novalidate>
<input type="text" id="name" required /> //
<input type="number" placeholder="Tax: " required name="tax" id="tax" />
<input type="number" placeholder="Tip: " required name="tip" id="tip" />
<button ng-click="submission()" ng-disabled="paymentForm.$invalid">Submit</button>
</form>
//inside controller
this.submission = function() {
//check if tip + tax is over 20
//prevent form and show error message if not
//otherwise allow default behavior
}
So I only want the form to actually submit if the tax/tip is over 10. How do I check for this and how do I prevent the form submission if it doesn't meet the requirements? Also, would I put this logic in the controller?
It looks pretty close to what you're after to me. Just a couple of things...
Add ng-model directives to your input controls to create two-way data-bindings that you can pick up and use in your controller:
<form name="cashForm" id="cashForm" novalidate>
<input id="name" name="name" type="text" ng-model="nameValue" required />
<input id="tax" name="tax" type="number" ng-model="taxValue" placeholder="Tax: " required />
<input id="tip" name="tip" type="number" ng-model="tipValue" placeholder="Tip: " required />
<button
ng-click="submission()"
ng-disabled="paymentForm.$invalid">
Submit
</button>
Inject $scope into your controller to allow you to pick up those ng-model bindings in your controller's submission method:
function submission() {
$scope.errorMsg = "";
if ($scope.taxValue <= 10) {
$scope.errorMsg = "tax not greater than 10";
return;
}
if ($scope.tipValue <= 10) {
$scope.errorMsg = "tip not greater than 10";
return;
}
// If you reached here your post-click validation passed,
// so continue to submit the data...
}
You could then display an error message using the ng-if directive with a css class that highlights the error message:
<div ng-if="!!errorMessage" class="text-danger">
{{ errorMessage }}
</div>
Finally, once you've cracked using $scope in your controller you might want to read about the perceived evils of using $scope and consider switching to controller-as syntax instead. Check out John Papa's Blog Post AngularJS's Controller As and the vm Variable

How to reset validation after submitting a form in AngularJS?

I have one input field:
<input type="text" name="title" ng-model="genreData.title" class="form-control"
ng-class="{'error': addGenreForm.title.$invalid && !addGenreForm.title.$pristine}"
placeholder="Genre name" ng-minlength="minlength" required autofocus>
When I succesfully submit form this input is got class="error" after this:
$scope.genreData = {};
How can I fix it?
You've got to inject the form in the ng-submit function and then call the form's controller built in function $setPristine().
e.g.
View:
<form name="myForm" ng-submit="submitForm(myForm)">
<!--Input Fields-->
</form>
Controller:
$scope.submitForm = function(form) {
//Do what ever I have to do
//Then reset form
form.$setPristine();
}
I think setting
$scope.addGenreForm.$setPristine() and $scope.addGenreForm.$setUntouched
can work after submitting your form
Please share some plunker so I can help more if you still have any issues

issue when set parent form valid manually by setting each child form field valid

I have nested and repeated forms on UI.
<form class="form-horizontal" name="Registration">
<form ng-repeat = "s in students" name="studentRegisteration">
<input type="number" id="input" name="input" model="input" ng-change="validate()">
</form>
</form>
I am trying $setValidity() for a specific field of child form as following
$scope.validate = function (index) {
if (condition true)
$scope.Registration.studentRegisteration.input.$setValidity('integer', false);
}
It works fine until i have one form on UI i.e. successfully set integer (property) valid and eventually parent forms become valid.
It does not work when i have multiple forms on UI... The parent forms does not become valid... i am not sure how to set field valid for individual child form so that parent becomes valid.
Any idea?
I think if you pass the form controller in the function parameter for validate it will work. Something like
<form class="form-horizontal" name="Registration">
<form ng-repeat = "s in students" name="studentRegisteration">
<input type="number" id="input" name="input" model="input" ng-change="validate(Registration.studentRegisteration)">
</form>
</form>
Code:
$scope.validate = function (form) {
if (condition true)
form.input.$setValidity('integer', false);
}
I am not sure if you pass Registration.studentRegisteration or studentRegisteration, try both

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.

How to use Angular input fields email validation inside controller?

I'm trying to validate a variable as email inside Angular controller.
var valid = $filter('email')($scope.email);
The email filter doesn't exists, generates "unknown provider error". What's the correct way to access email validation from inside the controller?
Thanks.
Later edit: don't put me to create a custom filter, it must be a way using angular validation.
You can create a hidden input type email:
<input type="email" hidden id="emailValidator" required ng-model="x" />
Then in js set the value to that input and check validity:
var validator = $('#emailValidator')[0];
validator.value = "someemail#tovalidate.com";
return validator.checkValidity();
Also this documentation could be helpful: https://docs.angularjs.org/guide/forms
You could use form and prevent form submission by adding ng-disabled to the submit button:
<form name="form">
<input type="email" ng-model="email" name="email" required/>
<button type="submit"
ng-disabled="form.$invalid">Submit</button>
</form>
{{form.email.$valid}}
As the point of validation is to prevent submission and show error messages. I think it's enough to do it there instead of controllers where you handle your business logic.
DEMO
You can write your own filter:
angular.module(/**some module description here**/).
filter('email',function(){
var validateEmail = function(email) {
var re = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
return function(input){
return validateEmail(input);
};
});
Regular expression is borrowed from here

Resources