AngularJs: Form inside ng-if not accessible from controller - javascript

I have a form inside ng-if directive. I want to check the form validation in controller using $valid.
<div ng-if="paymentMethod == 12">
<form name="creditForm" id="cc-form" novalidate>
<div class="form-group">
<label for="cardNumber">Card Number</label>
<input type="text" autofocus class="form-control" name="card_number" ng-minlength="16" id="cardNumber" ng-model="creditCardNumber" required>
<div class="red-text" ng-messages="creditForm.card_number.$error" ng-if="creditForm.card_number.$dirty || creditForm.$submitted">
<div ng-message="required">##global.Card_Num_Required##</div>
<div ng-message="maxlength">##global.Card_Num_MinLength##</div>
<div ng-message="minlength">##global.Card_Num_MaxLength##</div>
<div ng-message="minlength">##global.Card_Num_Numeric ##</div>
</div>
</div>
and trying to check valid form in controller
if ($scope.$parent.creditForm.$valid) {
alert('valid');
} else {
alert('not valid');
}
but the form is not accessible from controller.

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.
you can go throgh this link doc and also my answer here answer
You can use ng-show instead of ng-if if its feasible to you.

It works fine when you use auxiliary object in your controller.
In Your Controller file
$scope.page = {
creditForm:null
};
in your HTML file
<div ng-if="paymentMethod == 12">
<ng-form name="page.creditForm" id="cc-form" novalidate>
<div class="form-group">
<label for="cardNumber">Card Number</label>
<input type="text" autofocus class="form-control" name="card_number" ng-minlength="16" id="cardNumber" ng-model="creditCardNumber" required>
<div class="red-text" ng-messages="page.creditForm.card_number.$error" ng-if="page.creditForm.card_number.$dirty || page.creditForm.$submitted">
<div ng-message="required">##global.Card_Num_Required##</div>
<div ng-message="maxlength">##global.Card_Num_MinLength##</div>
<div ng-message="minlength">##global.Card_Num_MaxLength##</div>
<div ng-message="minlength">##global.Card_Num_Numeric ##</div>
</div>
</div>
</ng-form>
in this model you can saftly use ng-form inside ng-if

This happens because ngIf creates a new scope, so name="form" will register the form controller on that new scope (which is a child of the ctrl scope). You can solve this, by binding to an existing object on the target scope. (Alternatively, you can bind to the controller and use the controllerAs syntax, which is generally considered a good practice.)
This is because of how prototypal inheritance works in JavaScript.
You can find a sample plunker here
<form name="data.form" novalidate ng-if=true>
http://plnkr.co/edit/2Ip3gNPdUWWV0zK8PNJr?p=preview

$scope.$watch('creditForm.$valid', function(newVal) {
//$scope.valid = newVal;
alert('valid');
});

Related

AngularJS apply class to parent element on event?

I have a form field:
<div class="form-group">
<label>Name</label>
<input type="text" name="name" class="form-control" ng-model="abc.user.name" ng-focus="abc.setFocus('name')" required>
</div>
What I need to do is set add a class to the parent element, here <div class="form-group">, when the input has focus and remove it when the field loses focus.
I know how to do this in jQuery, but not in an Angular way. I have many form fields that need to behave like this, so I'm trying to avoid setting a variable and looking for that with an ng-class. I'd rather have some way for the field to simple act on its parent, which I can use the same method in every form field.
A directive is possibly the simplest generic approach if all you need to do is manipulate the dom.
<div class="form-group" focus-class="focused">
<label>Name</label>
<input name="name" class="form-control" ng-model="abc.user.name" required>
</div>
JS
angular.module('myApp').directive('focusClass', function(){
return {
link:function(scope, elem, attrs){
elem.find('input').on('focus', function(){
elem.toggleClass(attrs.focusClass);
}).on('blur', function(){
elem.toggleClass(attrs.focusClass);
});
}
}
});
You can perform this
<div class="form-group {{focusIsSet ? 'is-focused': ''}}">
<label>Name</label>
<input type="text" name="name" class="form-control" ng-model="abc.user.name" ng-focus="focusIsSet = true" ng-blur="focusIsSet = false" required>
</div>
Where $scope.focusIsSet a boolean property. So depends of its state you can manage classes in <div class="form-group"> with that expression {{focusIsSet ? 'is-focused': ''}}
You change it with ng-focus and ng-blur directives
UPDATE
I think you can hold states for each input with that way
<div class="form-group {{checkFocusState('abc.user.name') ? 'is-focused': ''}}">
<label>Name</label>
<input type="text" name="name" class="form-control" ng-model="abc.user.name" ng-focus="setFocus('abc.user.name')" ng-blur="setBlur('abc.user.name')" required>
</div>
</div>
JS code
var inputsFocusState = {};
$scope.checkFocusState = function(propertyPathName) {
if(inputsFocusState[propertyPathName] == true) {
return true;
}
return false
}
$scope.setBlur = function(propertyPathName) {
inputsFocusState[propertyPathName] = false;
}
$scope.setFocus = function(propertyPathName) {
inputsFocusState[propertyPathName] = true;
}
Otherwise, you can create each focus property for each input in html template
P.S. ng-class is good option too
P.S.S I had similar case, but forms were completely dynamic.
So I split each property in object like user.name = {value: 'john', buttons: [...], label: 'Name', //and much more}.
Also better to change 'user.name.path' to something like 'user-name-path'.

View form surroundet with ng-if is clearing model

I'm trying to send some data from a form to my controller. Surrounded in the template file with an if. So my working example (without ng-if) is:
View
<form ng-submit="activate()">
<div class="md-form-group">
<input type="password" name="password" ng-model="input.password" required>
<label>Password</label>
</div>
<button type="submit" class="btn primary btn-block p-x-md">Submit</button>
</form>
Controller
$scope.showDescription1 = false;
$scope.activate = function() {
console.log($scope.input);
return;
...
}
Result
It is what I expect... The model object with password field. So now the problem: When I wrapped all the view with an ng-if, the console.log will write undefined.
Modified view
<div ng-if="!showDescription1" class="m-b text-sm">
<span>
This is the second description...
</span>
<form ng-submit="activate()">
<div class="md-form-group">
<input type="password" name="password" ng-model="input.password" required>
<label>Password</label>
</div>
<button type="submit" class="btn primary btn-block p-x-md">Submit</button>
</form>
</div>
Controller
(no change)
Result
The console.log call will write the undefined value. I don't know why, but the ng-if statement is clearing my model? However why?
ng-if will attach the element to DOM when the expression ng-if="!showDescription1" is evaluated as true and removes the element when it evaluates to `false'. For this reason any of your user input will be lost.
For your case, you need to use ng-show or ng-hide which will not re-render the element and instead change their display. So the user input persists.
ng-if creates its own child scope whenever it evaluates to true. So the value of "input" you are logging in console.log is inside the new child scope created by the ng-if and not the controller scope.
Below description from angular documentation here explains it :
Note that when an element is removed using ngIf its scope is destroyed
and a new scope is created when the element is restored. The scope
created within ngIf inherits from its parent scope using prototypal
inheritance. An important implication of this is if ngModel is used
within ngIf to bind to a javascript primitive defined in the parent
scope. In this case any modifications made to the variable within the
child scope will override (hide) the value in the parent scope.
Now, to get your code working , use $parent to the model as shown below :
<div ng-if="!showDescription1" class="m-b text-sm">
<span>
This is the second description...
</span>
<form ng-submit="activate()">
<div class="md-form-group">
<input type="password" name="password" ng-model="$parent.input.password" required>
<label>Password</label>
</div>
<button type="submit" class="btn primary btn-block p-x-md">Submit</button>
</form>
Try this it is working as per your expectation :
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl',function($scope) {
$scope.showDescription1 = false;
$scope.activate = function() {
console.log($scope.input);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller="MyCtrl">
<div ng-if="!showDescription1" class="m-b text-sm">
<span>
This is the second description...
</span>
<form ng-submit="activate()">
<div class="md-form-group">
<label>Password</label>
<input type="password" name="password" ng-model="input.password" required>
</div>
<button type="submit" class="btn primary btn-block p-x-md">Submit</button>
</form>
</div>
</div>

Angular $setPristine() not working

I'm trying to use Angular's built-in form functions, specifically setPristine() to clear the form on user submit. My controller has access to $scope.newForm (my form) with all of its methods, but running $scope.newForm.$setPristine() isn't resetting the form fields.
Here is my HTML:
<div ng-controller="NewFormController">
<h3>New Entry</h3>
<form name="newForm" method="post" novalidate>
<div class="input-group">
<label>Name</label>
<input name="name" type="text" ng-model="place.name"/>
</div>
<div class="input-group">
<label>Description</label>
<textarea name="description" type="text" ng-model="place.description"></textarea>
</div>
<div class="input-group">
<label>Neighborhood</label>
<input name="neighborhood" type="text" ng-model="place.neighborhood"/>
</div>
<div class="input-group">
<label>Address</label>
<input name="location" type="text" ng-model="place.address"/>
</div>
<input type="submit" value="Submit" ng-click="submit(place)"/>
</form>
</div>
And here is the controller where I call setPristine():
app.controller('NewFormController', function($scope, $compile) {
$scope.place = {
name: 'ExamplePlace',
description: 'This is a description!',
neighborhood: 'Manhattan',
address: '112 Street Place'
};
$scope.submit = function(place) {
$scope.newForm.$setPristine();
$scope.newForm.$setUntouched();
};
});
Here is a working codepen that reproduces my problem.
Note: I'm using Angular version 1.4.3.
$setPristine only marks the form as being $pristine, which is useful for validation-driven expressions and CSS (e.g. .ng-dirty)
So, $setPristine does not clear the form's controls. In fact, it wouldn't even know how to do that. Consider, that to "clear" could mean different things to different models. "Clear" could mean "", or undefined, or null, or anything at all that a custom input control that works with ngModel could mean.
So, to properly clear the form is to modify the View Model that drives the form to whatever definition of "clear" it needs. In most cases - yours included - it is just a matter of setting the View Model to a new object:
$scope.submit = function(place) {
$scope.newForm.$setPristine();
$scope.newForm.$setUntouched();
// clear the form
$scope.place = {};
};

How to add data from scope to select box

I'm fairly new to angular, so hopefully this is a super simple question for someone to nail.
I have a form (cut down version below) that I want to be able to have a live preview being shown as the user fills in the form.
All was going well with standard fields, however I've hit a roadblock with <select> fields.
<div ng-app="jobcreate">
<div class="row fullWidth" ng-contoller="JobCtrl">
<div class="medium-6 columns">
<form method="POST" action="http://localhost:3030/job/create" accept-charset="UTF-8">
<label for="title">Enter a title</label>
<input placeholder="title" id="title" required="required" ng-model="job.title" name="title" type="text" />
<br />
<label for="title">Pick template</label>
<select ng-model="job.template" ng-options="template.Name for template in templates" name="template"></select>
</form>
</div>
<div class="medium-6 columns">
<div class='job-detail {{ job.template || "default" }}'>
<h2>{{ job.title || "Enter a title"}}</h2>
<h2>{{ job.template || "Pick a template"}}</h2>
<pre>Templates: {{templates | json}}</pre>
</div>
</div>
</div>
</div>
And here is the js:
angular.module('jobcreate', []).controller('JobCtrl', function($scope) {
$scope.templates = [
{ID:'default', name:'Default'},
{ID:'obnoxious', name:'Obnoxious'}
];
});
I have a jsfiddle here so you can see it in action: http://jsfiddle.net/2m8jm/4/
As you can see, entering something in the title field works as intended, but I'm struggling to get the contents of the $scope.colors to fill in the select field
In your fiddle : http://jsfiddle.net/2m8jm/4/, you have choosed templates as an data array for ng-options but there is not scope variable named templates in the controller JobCtrl. I have renamed $scope.colors to $scope.templates and modified the ng-options bit - ng-options="template.ID as template.name for template in templates".
Here is a working plunker : http://plnkr.co/edit/wsbxkjRqTEU2yfcHOV0D?p=preview
Update
Is there a way to not have the first empty value be in the select field?
Yes, Couple of ways.
1) Initialize job.template with some default value in your markup as :
<label for="title" ng-init="job.template='obnoxious'">Pick template</label>
<select ng-model="job.template" ng-options="template.ID as template.name for template in templates" name="template"></select>
2) Define controller as follows to set default value for job.template inside the controller :
.controller('JobCtrl', function($scope) {
// some other codes
$scope.job = {};
$scope.job.template = 'default';
});

Why is my angularjs directive causing two-way binding to fail?

I can't figure out why when my directive is enabled on an element, the two-way binding fails.
Consider this plunkr
On the first removing the tooltip={{input1Error}} makes the input1 variable update as soon as you type in a valid email.
When tooltip={{input1Error}} is added, when typing in a valid email, the input1 model is never updated.
What am I missing?
There is a documented issue with the scope of the controller. You can get around this by implementing the changes below.
Change your controller to this:
app.controller('ctrl1', ['$scope','$log', function($scope, $log) {
$scope.model = {};
}]);
And the form to:
<form name="myForm" novalidate>
<div class="form-group">
<label>Input 1 *</label>
<input
class="span2"
name="input1id"
type="email"
ng-model="model.input1"
tooltip="{{model.input1error}}"
tooltip-placement="bottom"
tooltip-trigger="openPopup"
tooltip-trigger-on='openPopup'
tooltip-trigger-off='closePopup'
tooltip-show="myForm.input1id.$invalid"
required
/>
<pre>Input 1 is invalid: {{myForm.input1id.$invalid}}</pre>
<pre>Input 1 valid email: {{!myForm.input1id.$error.email}}</pre>
<pre>Input 1 error msg: {{model.input1error}}</pre>
</div>
<span class='error hidden' error-on="!myForm.input1id.$error.email" error-for='input1error'>Please enter a valid email</span>
<span class='error hidden' error-on="!myForm.input1id.$error.required"" error-for='input1error'>This field is required</span>
</form>

Categories

Resources