Angular Form Validation correction - angularjs

Here's Fiddle Link
Update--> works great with angular 1.3 by setting ng-model-options but i am using 1.2 :(
Here's Fiddle Link
Currently when user click on submit error comes up and goes away once he entered valid input. But What i want is not to hide the errors as user enter valid info.If user re-enter the correct data errors should go away only on clicking submit button only.
HTML
<div ng-app="app" ng-controller="MainCtrl">
<div class="panel-group">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">Order Form</h4>
</div>
<div class="panel-body">
<ng-include src="'form.html'"></ng-include>
</div>
</div>
</div>
<!-- kept seperate from the bootstrap markup to keep this example clean -->
<script type="text/ng-template" id="form.html">
<form name="form" valid-submit="sendForm()" novalidate>
<!-- call name-->
<div class="form-group clearfix" ng-class="{
'has-error': form.$submitted && form.orderNumber.$invalid,
'has-success': form.$submitted && form.orderNumber.$valid}">
<label class="col-sm-2 control-label" for="callName">Order Number</label>
<div class="col-sm-5">
<input id="orderNumber" name="orderNumber" class="form-control" type="text" ng-model="auth.orderNumber" ng-minlength="7" ng-maxlength="10" ng-pattern="/^[0-9]*$/" required ></input>
<div class="alert alert-danger" ng-show="form.$submitted && form.orderNumber.$error.required">Order Number required</div>
<span ng-show="form.$submitted && form.orderNumber.$error.pattern" class="help-block">Order number contains only numbers</span>
<span ng-show="form.$submitted && form.orderNumber.$error.minlength " class="help-block">Order number is too short</span>
<span ng-show="form.$submitted && form.orderNumber.$error.maxlength" class="help-block">Order number is too long</span>
</div>
</div>
<!-- last name-->
<div class="form-group clearfix" ng-class="{
'has-error': form.$submitted && form.confirmorderNumber.$invalid,
'has-success': form.$submitted && form.confirmorderNumber.$valid}">
<label class="col-sm-2 control-label" for="confirmorderNumber">Confirm Order Number</label>
<div class="col-sm-5">
<input id="lastName" name="confirmorderNumber" class="form-control" type="text" ng-model="person.lastName" data-match="auth.orderNumber" required></input>
<div class="alert alert-danger" ng-show="form.$submitted && form.confirmorderNumber.$error.required">required</div>
<span ng-show="form.$submitted && form.confirmorderNumber.$error.match" class="help-block">Order numbers must match</span>
</div>
</div>
<!-- form controls-->
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit!</button>
</div>
</form>
</script>
</div>
js
var app = angular.module('app', []);
// directive that prevents submit if there are still form errors
app.directive('validSubmit', [ '$parse', function($parse) {
return {
// we need a form controller to be on the same element as this directive
// in other words: this directive can only be used on a <form>
require: 'form',
// one time action per form
link: function(scope, element, iAttrs, form) {
form.$submitted = false;
// get a hold of the function that handles submission when form is valid
var fn = $parse(iAttrs.validSubmit);
// register DOM event handler and wire into Angular's lifecycle with scope.$apply
element.on('submit', function(event) {
scope.$apply(function() {
// on submit event, set submitted to true (like the previous trick)
form.$submitted = true;
// if form is valid, execute the submission handler function and reset form submission state
if (form.$valid) {
fn(scope, { $event : event });
//form.$submitted = false;
}
});
});
}
};
}
]);
app.directive('match', function () {
return {
require: 'ngModel',
restrict: 'A',
scope: {
match: '='
},
link: function(scope, elem, attrs, ctrl) {
scope.$watch(function() {
var modelValue = ctrl.$modelValue || ctrl.$$invalidModelValue;
return (ctrl.$pristine && angular.isUndefined(modelValue)) || scope.match === modelValue;
}, function(currentValue) {
ctrl.$setValidity('match', currentValue);
});
}
};
});
// handle form submission when the form is completely valid
app.controller('MainCtrl', function($scope) {
$scope.sendForm = function() {
alert('form valid, sending request...');
};
});

Related

AngularJS - number format directive not working with ng-show

I have used a directive to format number on textbox.
'use strict';
app.directive('numberOnly', function ($filter) {
return {
require: '?ngModel',
link: function (scope, elem, attrs, ctrl) {
if (!ctrl) {
return;
}
ctrl.$formatters.push(function () {
return $filter('number')(ctrl.$modelValue);
});
elem.bind('blur', function (event) {
var plainNumber = elem.val().replace(/[^\d|\-+|\.+]/g, '');
elem.val($filter('number')(plainNumber));
});
}
};
});
And in the html I was use:
<div class="row" ng-show="step === 1">
<div class="col-lg-6">
<label for="">Number1</label>
<div class="form-group">
<input type="text" class="form-control" number-only ng-model="mynumber1">
</div>
</div>
</div>
<div class="row" ng-show="step === 2">
<div class="col-lg-6">
<label for="">Number2</label>
<div class="form-group">
<input type="text" class="form-control" number-only ng-model="mynumber2">
</div>
</div>
</div>
In controller I set step = 1 as default. So when page loaded I see in textbox Number1 the value display as expected: 23,456
But when I change step = 2 to show the Number2 then the value in this textbox is 23456 (it not formatted).
Please let me know why it is and how to resolved it?

Angular validation directive not working properly

I am using custom angular directive for showing validation. Directive code is as below
angularFormsApp.directive('showErrors',['$timeout', function ($timeout) {
return {
restrict: 'A',
require: '^form',
link: function (scope, el, attrs, formCtrl) {
// find the text box element, which has the 'name' attribute
var inputEl = el[0].querySelector("[name]");
// convert the native text box element to an angular element
var inputNgEl = angular.element(inputEl);
// get the name on the text box so we know the property to check
// on the form controller
var inputName = inputNgEl.attr('name');
var helpText = angular.element(el[0].querySelector(".help-block"));
// only apply the has-error class after the user leaves the text box
inputNgEl.bind('blur', function () {
el.toggleClass('has-error', formCtrl[inputName].$invalid);
helpText.toggleClass('hide', formCtrl[inputName].$valid);
});
scope.$on('show-errors-event', function () {
el.toggleClass('has-error', formCtrl[inputName].$invalid);
});
scope.$on('hide-errors-event', function () {
$timeout(function () {
el.removeClass('has-error');
}, 0, false);
});
}
}
}]);
and Html is as below
<div class="container" id="login-form">
<img src="assets/img/logo-big.png">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<div class="panel panel-default">
<div class="panel-heading">
<h2>Login Form</h2>
</div>
<div class="panel-body">
<form name="loginForm" class="form-horizontal" novalidate>
<div class="form-group mb-md" show-errors>
<div class="col-xs-12">
<div class="input-group">
<span class="input-group-addon">
<i class="ti ti-user"></i>
</span>
<input type="text" class="form-control" placeholder="Username" autocomplete="Off" ng-required="true" name="username" autofocus ng-model="loginUser.username">
</div>
<span class="help-block" ng-if="loginForm.username.$error.required">Username is required</span>
</div>
</div>
<div class="form-group mb-md" show-errors>
<div class="col-xs-12" >
<div class="input-group">
<span class="input-group-addon">
<i class="ti ti-key"></i>
</span>
<input type="password" class="form-control" placeholder="Password"
name="password"
ng-model="loginUser.password" autocomplete="Off"
ng-required="true">
</div>
<span class="help-block" ng-if="loginForm.password.$error.required">Password is required</span>
</div>
</div>
<div class="form-group mb-n">
<div class="col-xs-12">
Forgot password?
<div class="checkbox-inline icheck pull-right p-n">
<label for="">
<input type="checkbox"></input>
Remember me
</label>
</div>
</div>
</div>
</form>
</div>
<div class="panel-footer">
<div class="clearfix">
Register
Login
</div>
</div>
</div>
</div>
</div>
</div>
Controller code :
var loginController = function ($scope, $window, $routeParams, $uibModal, $location, $filter, $rootScope, DataService, SharedProperties) {
$rootScope.bodylayout = 'focused-form animated-content';
$scope.loginUser = {
username: "",
password:""
}
$scope.load = function () {
//getAppointmentInfo();
};
$scope.SubmitLoginForm = function () {
$scope.$broadcast('show-errors-event');
if ($scope.loginForm.$invalid)
return;
}
}
angularFormsApp.controller("loginController", ["$scope", "$window", "$routeParams", "$uibModal", "$location", "$filter", "$rootScope", "DataService", "SharedProperties", loginController]);
Now When I open form below input control validation span is displaying by default . When I click on Login button then its showing in red and working fine.
problem is it shouldn't show by default when Page is opend.. Please see image below
Instead of this
loginForm.password.$error.required
try this
(loginForm.$submitted || loginForm.username.$dirty) && loginForm.password.$error.required
Take a look at the ng-messages directive. Its fairly elegant. Example:
<form name="myForm">
<input type="text" ng-model="field" name="myField" required minlength="5" />
<div ng-messages="myForm.myField.$error">
<div ng-message="required">You did not enter a field</div>
<div ng-message="minlength">The value entered is too short</div>
</div>
</form>
You can then combine it with any form validation. Just place the error messages from the validators onto the elements $error object and they are automatically rendered in your UI.
I ended up here as part of my search for an issue. In my case I was using a directive with a changing minimum value like this:
ngOnChanges(changes: SimpleChanges) {
if (changes.minDate && this.control) {
this.control.updateValueAndValidity({ onlySelf: true });
}
}
This means that the form will not be updated, I removed onlySelf and it worked correctly.
this.control.updateValueAndValidity();
Just leaving this as a breadcrumb in case someone else does something similar.

Custom validation depending on another input

The idea is very simple. I have two inputs, and I validate them only if both are touched. At least one has to be different than 0. Where am I going wrong?
The HTML
<div class="form-group" ng-class="{'has-error' : (myForm.$submitted || myForm.for.$touched) && myForm.for.$invalid }">
<label class="col-md-4 control-label" for="for">For</label>
<div class="col-md-8">
<input class="form-control" id="for" name="for" ng-model="item.for" ng-required="(some expression)" type="number" one-not-zero="item.against" one-not-zero-touched="myForm.against.$touched"/>
<div ng-messages="myForm.for.$error" role="alert" ng-show="(myForm.$submitted || myForm.for.$touched) && myForm.for.$invalid">
<div class="help-block" ng-message="required">
REQUIRED
</div>
<div class="help-block" ng-message="oneNotZero">At least one has to be different than 0</div>
</div>
</div>
</div>
<div class="form-group" class="{'has-error' : (myForm.$submitted || myForm.against.$touched) && myForm.against.$invalid }">
<label class="col-md-4 control-label" for="against">Against</label>
<div class="col-md-8">
<input class="form-control" id="against" name="against" ng-model="item.against" ng-required="(some expression) " type="number" one-not-zero="item.for" one-not-zero-touched="myForm.for.$touched"/>
<div ng-messages="myForm.against.$error" role="alert" ng-show="(myForm.$submitted || myForm.against.$touched) && myForm.against.$invalid">
<div class="help-block" ng-message="required">
REQUIRED
</div>
<div class="help-block" ng-message="oneNotZero">At least one has to be different than 0</div>
</div>
</div>
</div>
The Directive
var oneNotZero = function() {
return {
restrict: "A",
require: "ngModel",
scope: {
oneNotZero: "=",
oneNotZeroTouched: "="
},
link: function(scope, element, attrs, ctrl) {
ctrl.$validators.oneNotZero = function (modelValue, viewValue) {
return scope.oneNotZeroTouched ? !((viewValue == 0) && (scope.oneNotZero == 0)) : true;
};
}
};
};
myModule.directive("oneNotZero", oneNotZero);
EDIT: I made some progress, changed the code to my latest version, but it doesn't fully work. If I don't make any changes to the values, and just go trough the inputs until I get to the submit button and hit enter, the validation doesn't fire. SHould I $watch the changes on the inputs?
This will submit the form if at least one field is not 0 else will display an error message.
<form name="myForm" novalidate>
<input name="for" ng-model="item.for" type="number" required>
<input name="against" ng-model="item.against" type="number" required>
<input type="submit" value="Submit">
<div ng-show="myForm.$submitted && (item.for == 0 || item.against == 0)">
At leat one field must be different from 0.
</div>
</form>

Bootstrap modal form elements unfocussable (on mobile/mobile emulation)

From the repo: https://github.com/jmsherry/kitchenapp
Live at: https://kitchenapp2.herokuapp.com
If you click the 'contact us' link in the bottom right corner of the screen a modal appears. The modal is coded like:
<div class="modal-container">
<button type="button" class="close" aria-label="Close" ng-click="$close(null)">
<span aria-hidden="true">×</span>
</button>
<div class="content">
<h1>Contact Us</h1>
<div class="contact-form">
<form ng-submit="vm.sendMessage(this)" novalidate name="contactForm">
<div class="form-group" ng-if="vm.user && vm.user.email">
<p class="faux-label">From: <span class="faux-value">{{vm.user.email}}</span></p>
</div>
<div class="form-group" ng-class="{ 'has-error' : contactForm.from.$invalid && !contactForm.from.$pristine }" ng-if="!vm.user || !vm.user.email">
<label for="subject">From</label>
<input type="email" name="from" id="from" class="form-control" placeholder="From" ng-model="vm.user.email" ng-maxlength="30" ng-required="true" autofocus>
<ul ng-if="contactForm.from.$invalid && !contactForm.from.$pristine" ng-messages="contactForm.from.$error" class="help-block list-unstyled">
<li ng-message="maxlength">The subject must be less than 30 characters.</li>
<li ng-message="email">Not a valid email. Please check carefully and try again...</li>
</ul>
</div>
<div class="form-group" ng-class="{ 'has-error' : contactForm.subject.$invalid && !contactForm.subject.$pristine }">
<label for="subject">Subject</label>
<input type="text" name="subject" id="subject" class="form-control" placeholder="Subject" ng-model="vm.email.subject" ng-maxlength="30">
<ul ng-if="contactForm.subject.$invalid && !contactForm.subject.$pristine" ng-messages="contactForm.subject.$error" class="help-block list-unstyled">
<li ng-message="maxlength">The subject must be less than 30 characters.</li>
</ul>
</div>
<div class="form-group" ng-class="{ 'has-error' : contactForm.message.$invalid && !contactForm.message.$pristine }">
<label for="message">Message</label>
<textarea name="message" id="message" class="form-control" placeholder="Message..." ng-model="vm.email.body" ng-required="true" ng-minlength="10" ng-maxlength="500"></textarea>
<ul ng-if="contactForm.message.$invalid && !contactForm.message.$pristine" ng-messages="contactForm.message.$error" class="help-block list-unstyled">
<li ng-message="required">A description is required.</li>
<li ng-message="minlength">The name must be longer than 10 characters.</li>
<li ng-message="maxlength">The name must be less than 500 characters.</li>
</ul>
</div>
<!-- <div class="form-group"> -->
<div class="checkbox">
<label for="cc_self">
<input type="checkbox" id="cc_self" name="cc_self" ng-model="vm.email.cc_self">Send a copy to my email.
</label>
</div>
<!-- </div> -->
<div class="form-group">
<div class="pull-right">
<button type="button" class="btn btn-default" ng-click="$close(null)">Cancel</button>
<button type="submit" class="btn btn-primary" ng-disabled="contactForm.$invalid">Send</button>
</div>
</div>
</form>
</div>
</div>
</div>
Controller looks like:
angular.module('kitchenapp.directives')
.controller('KaFooterCtrl', ['$scope', '$q', '$log', '$modal', 'Email', 'Auth', 'toastr', function ($scope, $q, $log, $modal, Email, Auth, toastr) {
var vm = this,
$user;
function contact(day) {
$log.log('contact fn', arguments);
$modal.open({
templateUrl: '/views/modals/contact-modal.html',
controller: function modalController($modalInstance, $scope) {
$scope.vm = this;
$scope.vm.email = vm.email;
$scope.vm.user = vm.user;
$scope.vm.sendMessage = function () {
$modalInstance.close();
$q.when(vm.user, function (user) {
var $emailSent = Email.sendEmail(vm.email, vm.user);
$q.when($emailSent, function (res) {
toastr.success('Email sent! Thank you for your interest...');
}, function () {
toastr.error('We\'re sorry your email hasn\'t been sent, please try again later...');
});
});
event.preventDefault();
event.stopPropagation();
};
},
contollerAs: 'vm'
});
}
vm.year = new Date().getFullYear();
vm.email = {};
$user = Auth.getUser();
$q.when($user, function (user) {
vm.user = user;
});
vm.contact = contact;
vm.name = 'KaFooterCtrl';
}])
.directive('kaFooter', function () {
return {
restrict: 'E',
replace: true,
templateUrl: 'directives/footer/footer.html',
scope: {
companyName: '#',
companyEmail: '#'
},
controller: 'KaFooterCtrl as vm',
bindToController: true
};
});
}());
For some reason the first element focusses once, but any further interaction and the elements are not focussable by touch or mouse. Any clues why?
Thanks
So, it transpires that this is a problem when ng-touch is used. ng-touch auto-blurs the fields (https://github.com/angular/angular.js/pull/11212). A fix is on the way, but for now the user #jdhiro suggests using a directive to stop propagation of the touchend event outside of your modal. See https://github.com/angular-ui/bootstrap/issues/2017#issuecomment-39515923 for more

angular FormController validation

I have this html form:
<div class="modal-body">
<form name="addAdminForm">
<div class="form-group addPopupLabel">
<div class="container-fluid-full" id="email3">
<input placeholder="Email" type="text" ng-model="model.email" required />
</div>
<div class="container-fluid-full">
<input placeholder="Password (at last 6 characters)" type="password" ng-model="model.password1" id="pw1" name="pw1" required />
</div>
<div class="container-fluid-full">
<input placeholder="Confirm password" type="password" ng-model="model.password2" id="pw2" name="pw2" required password-compare="pw1" />
</div>
<div class="container-fluid-full">
<label>Admin</label>
<input type="radio" class="form-control" name="role" ng-model="model.role" ng-value="userRoles.admin">
</div>
<div class="container-fluid-full">
<label>CS</label>
<input type="radio" class="form-control" name="role" ng-model="model.role" ng-value="userRoles.cs">
</div>
<div>
<span class="errormessage" style="color:red">{{errormessage}}</span>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-disabled="addAdminForm.$invalid" ng-click="createForm.$invalid || ok()">Submit</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
The problem: button stay on disable mode while one of the fields is on focus.
How can i solve it by FormController?
The addAdminForm is scoped within the form. So one option is to move the buttons within the form, or move the form element so that it wraps the buttons. I would prefer this since it is dead simple and most times doable.
If it is not possible for some reason, you can make a directive to export the $invalid flag of a form to a variable of the outer scope. A simple implementation would be:
app.directive('bindValidity', ['$parse', function($parse) {
return {
restrict: 'A',
scope: false,
controller: ['$scope', '$attrs', function($scope, $attrs) {
var assign = $parse($attrs.bindValidity).assign;
if (!angular.isFunction(assign)) {
throw new Error('the expression of bindValidity is not settable: ' + $attrs.bindValidity);
}
this.setFormController = function(formCtrl) {
if (!formCtrl) {
throw new Error('bindValidity requires one of <form> or ng-form');
}
$scope.$watch(
function () {
return formCtrl.$invalid;
},
function (newval) {
assign($scope, newval);
}
);
};
}],
require: ['?form', '?ngForm', 'bindValidity'],
link: function (scope, elem, attrs, ctrls) {
var formCtrl, bindValidity;
formCtrl = ctrls[0] || ctrls[1];
bindValidity = ctrls[2];
bindValidity.setFormController(formCtrl);
}
};
}]);
Use it as:
<div class="modal-body">
<form name="addAdminForm" bind-validity="some.variable">
...
</form>
<div class="modal-footer">
<button ... ng-disabled="some.variable" ng-click="some.variable || ok()">Submit</button>

Resources