Cannot read property '$valid' of undefined - angularjs

I have a form with a name field and two validations - required and max-length.
When I enter correct value, the form in controller is showing valid and returning true. But on entering wrong data, $valid is not throwing false and simply says -
Cannot read property '$valid' of undefined
Why is the form getting undefined in this case?
Jsfiddle here
Thanks in advance

Add ng-submit attribute to the form:
<form name="myForm" ng-submit="SaveAndNext()" novalidate>
Change controller to:
function myCtrl($scope, $log){
$scope.data = {};
$scope.SaveAndNext = function(){
if($scope.myForm.$valid){
$log.info('Form is valid');
$log.info($scope.data);
} else {
$log.error('Form is not valid');
}
}
}
Remove ng-click event handler from your submit button
Updated fiddle: http://jsfiddle.net/9cgopo7d/1/
I included the $log service because it's a useful default service and you might not know about it.

See demo below pass your form to controller with the submit click
ng-click="SaveAndNext(myForm)"
and now you can easy access all your form properties
function myCtrl($scope) {
$scope.SaveAndNext = function(form) {
console.log(form);
if (form.$valid) {
alert(form.$valid);
} else {
alert("!" + form.$valid)
}
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="registrationdetails" ng-app="" ng-controller="myCtrl">
<form name="myForm" novalidate>
<div class="list">
<div class="item list-item">
<div class="row row-center">
<div class="col-50"> <span class="form-field-name">Name</span>
</div>
<div class="col-50">
<textarea type="text" rows="5" ng-model="data.Name" name="Name" ng-maxlength=45 required></textarea> <span ng-show="myForm.Name.$dirty && myForm.Name.$invalid">
<span class="form-error" ng-show="myForm.Name.$error.required">Name is required</span>
<span class="form-error" ng-show="myForm.Name.$error.maxlength">Name should be max 45 characters</span>
</span>
</div>
</div>
</div>
<div class="item list-item">
<div class="row row-center">
<div class="col-50"> <span class="form-field-name">Type</span>
</div>
<div class="col-50">
<select ng-class="{defaultoption: !data.Type}" ng-model="data.Type" name="Type" required>
<option value="" disabled selected class="defaultoption">Type</option>
<option value="1" class="actualoption">Type 1</option>
<option value="2" class="actualoption">Type 2</option>
</select>
<span ng-show="myForm.Type.$dirty && myForm.Type.$invalid">
<span class="form-error" ng-show="myForm.Type.$error.required">Type is required</span>
</span>
</div>
</div>
</div>
<div class="item list-item">
<div class="row">
<div class="col">
<input type="submit" class="button" ng-click="SaveAndNext(myForm)" value="Next">
</div>
</div>
</div>
</div>
</form>
</div>

Place $scope.data={}; at the top of controller:-)
This is happening becuase you are directly submit the form which doesn't yet initialized the data object and therefore name data.Name is not recognized.
And if you place $scope.data={} in that case we have empty data object initialized.So in second case we have atleast reference of empty object whereas in first case it doesn't have any reference.
Fiddle

HTML:
<form name="form" id='form' novalidate>
<input type="text"required name="Title" ng-model="Title">
<div ng-messages="form.Title.$error">
<div ng-message="required">Field Required</div>
</div>
</form>
JS:
$scope.save= function(){
if (!scope.createMessage.$valid) {
scope.form.$submitted = true;
return;
}
The format for using $valid should be like this. If you don't get again then visit the official site of AngularJs.

I had similar problem, but in my case I was a ng-if creating a child scope that did not have the form defined.
Just changed the ng-if to ng-show and the form stop be undefined.

Related

form.$valid is working for "required" but it's not working for ngPattern and maxlength.

If zipcode is empty then form is invalid so button is disabled but if zipcode is 2 digits error message is showing but form is showing as valid in controller. If zipcode is empty then I need to disable button but I'm checking form valid or not but dont worry about ng-disabled. I just need solution for showing the "div" if and only if form is valid.
function submitUserDetail (formValid) {
if(formValid) {
$scope.showDiv = true;
}
}
<div class="">
<div class="">
<label required>
Zip code required
</label>
<label pattern>
Invalid Zip code
</label>
</div>
<div class="">
<input type="tel" maxlength="5" class="" name="zip5"
ng-model="userDetail.zipCode" required=""
pattern="^\d{5}$"
data-validate-on-blur="true" value=""
size="5">
<span class="" title="Reset" onclick="jQuery(this).prev('input').val('').trigger('change');"></span>
</div>
</div>
<div class="">
<div class="">
<div class="">
<span class=""></span>
<button class="" href="#" id="button" ng-click="submitUserDetail(form.$valid)" ng-disabled="form.$invalid">See section</button>
<span class=""></span>
</div>
</div>
</div>
<div ng-if="showDiv">
.......
</div>
Thanks in advance.
I assume that you have a <form> wrapping the HTML provided.
If so, you should make sure your <form> tag has novalidate applied so you're using AngularJS form validation and not HTML5 form validation:
<form name="form" ng-submit="submitUserDetail(form.$valid)" novalidate>
Also, it looks like you're mixing HTML5 validation attributes with AngularJS validation attributes. You should be using ng-required and ng-pattern instead of required and pattern.

Angular validation for only 1 input field inside a form

I'm building a form using Angular 1.1.1 and Ionic.
There are many "wallets" and the user needs to send a new "value" to each of the wallet. My form has a validation for all fields which works fine when the 'submit' button for the form is pressed.
However, I also have a button next to each wallet to send only value to this wallet (not different values to all wallets). When I press it, all the validation errors appear, but I need error to be visible only for the particular wallet.
My form (index.html):
<form name="myForm" ng-submit="sendValues(wallets)" ng-controller="valuesCtrl" novalidate>
<div class="row" ng-repeat="wallet in wallets">
<div class="col item item-input-inset">
<label class="item-input-wrapper item-text-wrap">
<input name="wallet_{{wallet.id}}" type="number" ng-model="wallet.value" type="text" required/>
</label>
<span ng-show="myForm.wallet_{{wallet.id}}.$error.required">!!!</span>
</div>
<div class="col item">{{ wallet.previous }}</div>
<button ng-click="sendValue(wallet)">
<i class="ion-android-send"></i>
</button>
<span class=ng-show="myForm.$submitted==true && myForm.wallet_{{wallet.id}}.$error.required">Required</span>
</div>
<button class="button" type="submit">Submit</button>
</form>
My controller (values.js):
'Use Strict';
angular.module('App')
.controller('valuesCtrl', function($scope, $localStorage, UserService, $state) {
$scope.sendValues = function(wallets){
if ($scope.myForm.$valid) {
...
} else {
$scope.myForm.submitted = true;
}
},
$scope.sendValue = function(wallet){
if (wallet.value == null) {
$scope.myForm.submitted = true;
} else {
...
}
}
})
You need to create a form for each wallet
This is due to your html name attributes has same value inside ng-repeat
Use $index in your name field for differentiate all the name attribute.
<form name="myForm" ng-submit="sendValues(wallets)" ng-controller="valuesCtrl" novalidate>
<div class="row" ng-repeat="wallet in wallets">
<div class="col item item-input-inset">
<label class="item-input-wrapper item-text-wrap">
<input name="wallet_{{$index}}" type="number" ng-model="wallet.value" type="text" required/>
</label>
<span ng-show="myForm.wallet_{{wallet.id}}.$error.required">!!!</span>
</div>
<div class="col item">{{ wallet.previous }}</div>
<button ng-click="sendValue(wallet)">
<i class="ion-android-send"></i>
</button>
<span class=ng-show="myForm.$submitted==true && myForm.wallet_{{$index}}.$error.required">Required</span>
</div>
<button class="button" type="submit">Submit</button>
</form>
You have to create a form inside form again. But as per HTML standard you can not have nested form. But angular provided that ability to have nested form but the inner form should be ng-form. Which mean you are going to wrap form & inside that you can find multiple ng-form's.
So you should have ng-form="innerForm" which will keep track of each repeated form.
Other thing which I observed is, you did mistake while using ng-show(you had {{}} inside ng-show expression, which would not work). To fix it you could access object via its key like ng-show="innerForm['wallet_'+wallet.id].$error.required"
Markup
<form name="myForm" ng-submit="sendValues(wallets)" ng-controller="valuesCtrl" novalidate>
<div ng-form="innerForm" class="row" ng-repeat="wallet in wallets">
<div class="col item item-input-inset">
<label class="item-input-wrapper item-text-wrap">
<input name="wallet_{{wallet.id}}" type="number" ng-model="wallet.value" type="text" required/>
</label>
<span ng-show="innerForm['wallet_'+wallet.id].$error.required">!!!</span>
</div>
<div class="col item">{{ wallet.previous }}</div>
<button ng-click="sendValue(wallet)">
<i class="ion-android-send"></i>
</button>
<span class=ng-show="innerForm.$submitted==true && innerForm['wallet_'+wallet.id].$error.required">Required</span>
</div>
<button class="button" type="submit">Submit</button>
</form>

angularjs dynamic form field undefined scope

Hi I have done this to add dynamic form field
<div ng-repeat="wspVdcMuniTbl in wspVdcMuniTbls">
<div class="col-sm-5">
<label>VDC/Municipalities</label>
<select
ng-model="wspVdcMuniTbl.tbl_vdc_municipality_id[$index]"
options="municipalities"
ng-options="municipality.id as municipality.name for municipality in municipalities">
</select>
</div>
<div class="col-sm-5">
<label>Wards</label>
<input type="text"
ng-model="wspVdcMuniTbl.wards[$index]"
id="Location">
</div>
<div class="col-sm-2">
<label> </label>
<button ng-hide="$first" ng-click="removeServiceArea($index)">-</button>
</div>
</div>
<div class="col-sm-2">
<button ng-click="addServiceArea()">+</button>
and my js part is
$scope.wspVdcMuniTbls = [{}];
$scope.addServiceArea = function(){
$scope.wspVdcMuniTbls.push({});
}
$scope.removeServiceArea = function(index){
$scope.wspVdcMuniTbls.splice(index,1);
}
here adding dynamic form field is working fine but after submit console.log($scope.wspVdcMuniTbl) is undefined what will be the problem please help

Dynamic models using ng-if - AngularJS form

Here is my code:
As you can see, this will only get displayed if q.answer == 'Fail' - that is the value of a select box further up in the page.
If "Fail" has been selected, then the user can select a defect type which will be saved into question.failure.type.
My question is, if a user selects "Fail" and then selects a defect type - and then changes "Fail" to something else. Then question.failure.type is still retained, even though the ng-if is false now. How can I make it so that question.failure.type is no longer in the model if the ng-if is false?
<div class="panel panel-danger" ng-if="q.answer == 'Fail'">
<div class="panel-heading">
<h3 class="panel-title">Comments</h3>
</div>
<div class="panel-body">
<form novalidate name="failureForm">
<div class="row">
<div class="col-xs-12">
<select name='defect' class='form-control' ng-model='question.failure.type' ng-options='item for item in defectTypes' required>
<option value=''>Select a defect type...</option>
</select>
...
Edit: added new code as requested
<div ng-repeat="q in questions" ng-show="standard.serviceType">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">{{ $index + 1 }}. <span ng-bind="q.questionText"></span></h3>
</div>
<div class="panel-body">
<answer-field question="q"></answer-field>
</div>
</div>
... then the code in the example above appears
Assuming that the value of q.answer is assigned in an <input> element in your HTML, you can clear the value of question.failure.type based on any conditional expression using ng-change.
For example:
<input ng-bind='q.answer'
ng-change='question.failure.type =
(q.answer == 'Fail' ? question.failure.type : undefined)'
>
This is only a trivial example, but it is essentially setting the question.failure.type to either the value it already has or undefined, depending on if q.answer == 'Fail' or not.
Here is the code for your question, assuming some predefined values for your scope variable.
<html ng-app="myNoteApp">
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script>
angular.module('myNoteApp', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.q = {
answer : 'Fail'
};
$scope.defectTypes = ['Item1','Item2','Item3'];
}]);
</script>
<div ng-controller="ExampleController">
<form name="myForm">
<label>Value1:
<input type="checkbox" ng-model="q.answer" ng-true-value="'Pass'" ng-false-value="'Fail'">
</label><br/>
<tt>Checkbox value is {{q.answer}}</tt><br/>
</form>
<div class="panel panel-danger" ng-if="q.answer === 'Fail'">
<div class="panel-heading">
<h3 class="panel-title">Comments</h3>
</div>
<div class="panel-body">
<form novalidate name="failureForm">
<div class="row">
<div class="col-xs-12">
<select name='defect' class='form-control' ng-model='question.failure.type' ng-options='item for item in defectTypes' required>
<option value=''>Select a defect type...</option>
</select>
</div>
</div>
</form>
</div>
</div>
</div>
</html>
you should put in your controller
$scope.$watch('q.answer', function(newVal, oldVal){
if(newVal!='Fail') question.failure.type.length = 0;
})
or, put into ng-change method
<input type="text" ng-model="q.answer" ng-change="changing()"/>
in controller
$scope.chaging = function(){
if($scope.q.answer!='Fail') question.failure.type.length = 0;
}

Clear the previous input once the selection is selected

How would I clear the input box once the user select from dropdown using angularjs.
every time clear the input box once the selection is made.
here is what I put together the sample:
http://jsfiddle.net/abuhamzah/jb1j3w7x/
<div ng-app="myApp" ng-controller="MyCtrl">
<div class="col-xs-12">
<label class="col-xs-6 control-label">Type:</label>
<div class="col-xs-6">
<select name="type" ng-model="payment.type" ng-dropdown required ng-change="changeme(payment.type)" >
<option ng-option value="Cash">Cash</option>
<option ng-option value="Check">Check</option>
<option ng-option value="Money Order">Money Order</option>
</select>
</div>
</div>
{{ payment.cash }}
<div class="col-xs-12" id="cash" ng-if="payment.type == 'Cash'">
<div >
<label class="col-xs-6 control-label">Cash :</label>
<div class="col-xs-6">
</div>
</div>
</div>
<div class="col-xs-12" id="check" ng-if="payment.type == 'Check'">
<div >
<label class="col-xs-6 control-label">check :</label>
<div class="col-xs-6">
</div>
</div>
</div>
<div class="col-xs-12" id="money_order" ng-if="payment.type == 'Money Order'">
<div >
<label class="col-xs-6 control-label">money_order :</label>
<div class="col-xs-6">
</div>
</div>
</div>
<div class="col-xs-12" ng-if="payment.type == 'Check' || payment.type == 'Money Order' || payment.type == 'Cash'">
<div >
<div class="col-xs-6"><input type="number" class="form-control" ng-model="payment_input"/>{{payment.type}}</div>
</div>
</div>
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.changeme = function() {
alert(payment.type);
payment_input='';
}
}
You can do this more easily with $watch, which is designed for this purpose.
In your controller:
$scope.$watch('payment.type', function (newVal, oldVal) {
$scope.payment_input = 0;
// anything else you want to do when the payment type is changed
})
Now whenever payment.type is changed, these lines will be executed.
Here are more docs: https://docs.angularjs.org/api/ng/type/$rootScope.Scope
EDIT
Here is a working fiddle with your code: http://jsfiddle.net/jb1j3w7x/5/
You need to use ng-show instead of ng-if for input element container because the following reason
The ngIf directive removes or recreates a portion of the DOM tree
based on an {expression}. If the expression assigned to ngIf evaluates
to a false value then the element is removed from the DOM, otherwise a
clone of the element is reinserted into the DOM.
Approach 1: (using controller)
Working demo: http://jsfiddle.net/jb1j3w7x/2/
Approach 2: (you can update the model without controller)
Working demo: http://jsfiddle.net/jb1j3w7x/4/
Also,
You have missed to add $scope for updating the number field
Number field should be updated as number. we cant update as empty
text

Resources