AngularJS - exclude field from form validation / dirty - javascript

I have a checkbox inside ng-form rapped by HTML5 form that I want to exclude so it won't change my form $state($pristine or $dirty).
I have tried using ignoreDirty directive - Angular 1.2: Is it possible to exclude an input on form dirty checking?
and some other solutions and none work for ng-form inside HTML5 form, some example code:
<form name="mainForm" class="col-xs-10 form-validation" novalidate>
<ng-form name="innerForm">
<div>
<span>check my
<span ng-if="vm.utils.mode!=='readonly'">
<input type="checkbox"
ng-model="vm.utils.amalotSecond"
class="secondYearCheckBox">
</span>
</span>
</div>
</ng-form>
<form>

The ignoreDirty directive you provided works fine like in this demo fiddle. The following code example was taken from angular-1-2-is-it-possible-to-exclude-an-input-on-form-dirty-checking. Also try to avoid using nested forms which are not HTML conform. An form element can't have an element form. It will cause problems and errors e.g. the one you are currently facing.
View
<div ng-controller="MyCtrl">
<form name="test">
Watching dirty: <input ng-model="name" /><br />
Ignoring dirty: <select ng-model="gender" ignore-dirty>
<option>male</option>
<option>female</option>
</select><br />
dirty: {{test.$dirty}}
</form>
</div>
AngularJS application
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function ($scope) {
$scope.name = 'Superhero';
});
myApp.directive('ignoreDirty', [function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$setPristine = function() {};
ctrl.$pristine = false;
}
}
}]);

Related

Directives interfering with each other

I have 2 directives that are on the same level as follows:
function signUpForm(djangoAuth, Validate){
return{
restrict:'A',
controller:["$rootScope","$scope",function($rootScope, $scope){
$scope.submitFunction = function(formData){
//do stuff
}
}]
}}
function signInForm(djangoAuth, Validate){
return{
restrict:'A',
controller:["$rootScope","$scope",function($rootScope, $scope){
$scope.submitFunction = function(formData){
//do stuff
}
}]
}}
My HTML is as follows:
<div>
<div class="email_log_container">
<form name="signup_form" class="signup_form" sign-up-form
novalidate ng-submit="submitFunction(signup_form)">
<input type="submit" name="submit" value="Submit" >
</form>
</div>
<div class="email_log_container">
<form name="signin_form" class="signin_form" sign-in-form
novalidate ng-submit="submitFunction(signin_form)">
<input type="submit" name="submit" value="Submit" >
</form>
</div>
</div>
The problem is that when I click the submit button on the second form, it actually submits the first form which results in errors. So I went on to add isolate scopes to the directives, now what happens is that the functions and attributes attached to $scope in the controller are not being picked up now. For example ng-submit does not know about the submitFunction within the controller.
Can anyone help me with ideas on how to stop these two directives from interfering with each other?
While you are using a controller but no template for your directive this will not work. Try it like in this runnable demo fiddle and add the form as a template into your directive. In that way you will be able to handle all the $scope goodness in your directive controller itself. The template itself will be compiled and uses the directive controller.
View
<div ng-controller="MyCtrl">
<div class="email_log_container">
<sign-in-form></sign-in-form>
</div>
</div>
AngularJS application
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', function($scope) {
});
myApp.directive('signInForm', function signInForm(){
return{
restrict:'E',
replace: true,
template: '<form name="signin_form" class="signin_form" sign-in-form novalidate ng-submit="submitFunction(signin_form)"><input type="submit" name="submit" value="Submit" ></form>',
controller:["$rootScope","$scope",function($rootScope, $scope){
$scope.submitFunction = function(formData){
//do stuff
console.log('sdfsdfd');
}
}]
}});

Padding form into templateURL to be used with ngMessages

I'm new to angular and trying my hand at directives.
I would like to be able to use
<div ng-messages="form.$error">
<div ng-message="required">You did not enter a field</div>
</div>
Within a custom directive like so,
<div class="form-group">
<label class="form-label" for="s-input-{{name}}">
{{text.human}}
<span ng-if="text.subtext" class="form-hint">{{text.subtext}}</span>
</label>
<select ng-required="required" ng-model="model" class="form-control" id="s-input-{{name}}" name="{{name}}" ng-options="t for t in text.select"></select>
<div ng-messages="form.$error">
<div ng-message="required">You did not enter a field</div>
</div>
</div>
I can get it to work by passing using ng-messages below the directive element but not in it.
Although I really can't figure out how to pass the form in to the template so it can be accessed by ng-messages.
I've set up a plunker to show you my bare bones setup.
http://plnkr.co/edit/ELx6Fd2eLN4CJjEnzRGM?p=preview
I know I need to require the form with require: '^form', although I'm not sure how to use/access in the template URL.
I've been banging my head for a while so would really appreciate some help! Thanks.
When you require the form directive with require: '^form' you then access that FormController in the link function. You can then simply attach that controller to the directive's scope like so:
link: function(scope, element, attributes, formController) {
scope.form = formController;
}
Then reference it in the template:
<div ng-messages="form.$error">
<div ng-message="required">You did not enter a field</div>
</div>
You could always pass the form to your directive.
javascript
app.directive('selectGov', function (){
return {
restrict: 'E',
templateUrl: 'select.html',
scope: {
text: "=?",
model: "=",
required: "=",
selectItems: "=?",
name: "#?",
form: "="
},
};
});
html
<select-gov model="person.phoneNumberType" text="q.phoneType" required="true" name="phoneType" form="myForm"></select-gov>

How do you find first visible element with jqlite under AngularJS?

So, I have the following Angular directive to autofocus first input field on the current page of my Ionic application. It works great when the first field isn't hidden. But, if it's hidden by ng-hide, logic fails. So need to modify the element selector to find just for visible elements in my directive.
angular.module('myApp').directive('focusMe', function($timeout) {
return {
link: function(scope, element, attrs) {
$timeout(function() {
element.find('label')[0].focus();
}, 150);
}
};
});
Using above directive on a form element as follows,
<form name="signinForm" ng-submit="signinForm.$valid && doSignin(credentials)" novalidate focus-me>
So how should I change my jQLite find query to just look for the visible element? The find lookup is limited by tag name as per docs.
You can write something like this:
element[0].querySelector('input:not(.ng-hide)').focus();
jQLite only allows selecting by tag name. So we can use the pure Javascript version i.e. querySelector along with using :not selector.
See a working example below:
angular.module('myApp', [])
.directive('focusMe', function($timeout) {
return {
link: function(scope, element, attrs) {
$timeout(function() {
element[0].querySelector('input:not(.ng-hide)').focus();
}, 150);
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form focus-me ng-app="myApp">
<input type="text" name="firstName" ng-show="false" />
<input type="text" name="lastName" ng-show="true" />
<input type="text" name="email" />
</form>

Angular get the current form in the html

I'm wondering if it is possible to retrieve the current form in a html template.
To be clear, I actually would like to reuse some form fields this way:
<form name="myForm">
<div ng-include src="'templates/form-field/email.html'">
<div ng-include src="'templates/form-field/password.html'">
</form>
Here is templates/form-field/email.html:
<div>
<label>Email:</label>
<input type="email" name="email" ng-model="data.email" ng-required="true">
</div>
<div ng-show="myForm.email.$error.required && myForm.email.$touched" class="error">Email is required</div>
<div ng-show="myForm.email.$error.email && myForm.email.$touched" class="error">Email is not valid</div>
So this works well and allows me to re-use HTML components including error messages.
However, I have to to give a static name to my form (here "MyForm")
Is there any way to retrieve the current form inside the tags?
I'm quite new with angular so there may even be better ways to achieve the same behavior (i.e. re-use html components / error messages) that I'm not aware of.
I'm interesting in any solution as long as it does not include any 3rd parties.
Maybe using $scope to define the name of the form?
With ng-include it creates a child scope from the parent so the data is only shared one-way, from parent to child. You will need to create a directive instead and set scope to false, or not declare it at all since that is the default, so that the controller and the directive will share the same scope.
Email Directive:
directive('emailTemplate', [function(){
return {
templateUrl: 'templates/form-field/email.html',
link: function(scope, elem, attrs) {
}
};
}]);
Password Directive:
directive('passwordTemplate', [function(){
return {
templateUrl: 'templates/form-field/password.html',
link: function(scope, elem, attrs) {
}
};
}]);
HTML
<form name="myForm">
<email-template></email-template>
<password-template></password-template>
</form>
EDIT
Email Directive:
directive('emailTemplate', [function(){
return {
templateUrl: 'templates/form-field/email.html',
link: function(scope, elem, attrs) {
scope.form = scope[attrs.formName];
}
};
}]);
HTML
<form name="myForm">
<email-template form-name="myForm"></email-template>
<password-template form-name="myForm"></password-template>
</form>
Template
<div>
<label>Email:</label>
<input type="email" name="email" ng-model="data.email" ng-required="true">
</div>
<div ng-show="form.email.$error.required && form.email.$touched" class="error">Email is required</div>
<div ng-show="form.email.$error.email && form.email.$touched" class="error">Email is not valid</div>

AngularJS - form wrapper directive, for validation purpose

Consider the form:
<form class="form" name="myForm" ng-submit="submit()">
<div class="form-group" ng-class="{'has-error': myForm.title.$invalid}">
<label for="title">Title: </label>
<input id="title" name="title" class="form-control" ng-model="title"
required>
<div ng-messages="myForm.title.$dirty && myForm.title.$error" role="alert">
<div ng-message="required" class="alert small with-error-font">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
Title is required!
</div>
</div>
</div>
</form>
I want to achieve the following behaviour:
Validation error message should appear if user started typing and left the field empty (it's not pristine anymore). Otherwise, it shouldn't be visible till user clicks submit.
So I thought it could be done by using sort of 'aspect'/'interceptor' which simply checks if form is valid. If it is, then it should invoke ng-submit function. If not, then it should invoke $setDirty on each field (so that validation errors appear) and simply return.
I want to achieve it by creating ma own directive, like the following:
let validateAndSubmit = function (onValidationSuccessCallback) {
return function () {
// this is where validation logic should happen
onValidationSuccessCallback();
};
};
let ValidatingFormDirective = function () {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {
name: '#',
postValidationCallback: '&'
},
link: function (scope) {
scope.validateAndSubmit = validateAndSubmit(scope.postValidationCallback);
},
templateUrl: 'app/events/common/forms/validating-form.template.html'
}
};
export default ValidatingFormDirective;
and here's my directive template:
<form name="{{name}}" novalidate ng-submit="validateAndSubmit()" ng-transclude></form>
Problem:
Now if I change form to validation-form in my html, it seems like myForm is not recognized at all, because neither ngMessages block nor ng-class appears.
How can I fix it? Or maybe should I try completely different approach?

Categories

Resources