I have a three part date field I need to validate in angular js. I have got as far as creating a custom validation function, but I am having trouble designing the logic of how the fields should update each other's status.
How can I get all three form fields to sing off the same hymn sheet, and all show their status as either valid or invalid depending on the others?
Here is the fiddle: http://jsfiddle.net/4GsMm/1/
And code:
<div ng-app="myApp" ng-controller="myCtrl">
<form action="" name="myForm">
<div class="date-group">
<input type="text" name="day" ng-model="day" ng-valid-func="validator" />
<input type="text" name="month" ng-model="month" ng-valid-func="validator" />
<input type="text" name="year" ng-model="year" ng-valid-func="validator" />
</div>
</form>
</div>
and...
input.ng-invalid{
background-color: #fdd !important;
}
input.ng-valid{
background-color: #dfd !important;
}
input{
display: inline;
width: 3em;
}
and...
var app = angular.module('myApp', [])
var myCtrl = function($scope){
$scope.day = "01"
$scope.month = "01"
$scope.year = "2000"
$scope.validator = function(val){
var day = $('[name=day]').val()
var month = $('[name=month]').val()
var year = $('[name=year]').val()
var d = new Date([year,month,day].join('-'))
console.log(d, [year,month,day].join('-'))
return d > new Date('2000-01-01')
}
}
app.directive('ngValidFunc', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
if (attrs.ngValidFunc && scope[attrs.ngValidFunc] && scope[attrs.ngValidFunc](viewValue, scope, elm, attrs, ctrl)) {
ctrl.$setValidity('custom', true);
} else {
ctrl.$setValidity('custom', false);
}
return elm.val()
});
}
};
});
I too was looking for three part date field validation with AngularJS, here is what I did (working for me)
HTML:
<form name="myForm" method="post" novalidate ng-submit="somefuncion()" ng-controller="Controller" ng-app="testModule">
Date (MM-DD-YYYY): <input id="partnerDOBmm" name="partnerDOBmm" class="ddinput" type="text" value="" size="2" maxlength="2" ng-model="partnerDOBmm" required only-digits ng-minlength="2" ng-change="validateDOB(partnerDOBmm,partnerDOBdd,partnerDOByyyy)" />
-
<input id="partnerDOBdd" name="partnerDOBdd" class="ddinput" type="text" value="" size="2" maxlength="2" ng-model="partnerDOBdd" required only-digits ng-minlength="2" ng-change="validateDOB(partnerDOBmm,partnerDOBdd,partnerDOByyyy)" />
-
<input id="partnerDOByyyy" name="partnerDOByyyy" class="yyyyinput" type="text" value="" size="4" maxlength="4" ng-model="partnerDOByyyy" required only-digits ng-minlength="4" ng-change="validateDOB(partnerDOBmm,partnerDOBdd,partnerDOByyyy)" />
<br />
<span class="error" ng-show="submitted && (myForm.partnerDOBmm.$error.required || myForm.partnerDOBdd.$error.required || myForm.partnerDOByyyy.$error.required)"> Required! </span>
<span class="error" ng-show="submitted && (myForm.partnerDOBmm.$error.minlength || myForm.partnerDOBdd.$error.minlength || myForm.partnerDOByyyy.$error.minlength)"> Too Short! </span>
<span class="error" ng-show="notValidDate && !(myForm.partnerDOBmm.$error.required || myForm.partnerDOBdd.$error.required || myForm.partnerDOByyyy.$error.required || myForm.partnerDOBmm.$error.minlength || myForm.partnerDOBdd.$error.minlength || myForm.partnerDOByyyy.$error.minlength)"> Not a Valid Date! </span>
<br />
<button type="submit" class="btnSubmit" ng-click="submitted=true">Submit</button>
</form>
Script:
function Controller ($scope) {
$scope.notValidDate = false;
$scope.somefuncion = function(event) {
if ($scope.myForm.$valid) {
alert("Form is working as expected");
return true;
}
else
{
alert("Something is not correct");
return false;
}
};
$scope.validateDOB = function(mm,dd,yyyy)
{
var flag = true;
var month = mm;
var date = dd;
var year = yyyy;
if(month == null || date == null || year == null || month.length != 2 || date.length!= 2 || year.length!= 4)
flag = false;
if(month < 1 || month > 12)
flag = false;
if(year < 1900 || year > 2100)
flag = false;
if(date < 1 || date > 31)
flag = false;
if((month == 04 || month == 06 || month == 9 || month == 11) && (date >= 31))
flag = false;
if(month == 02)
{
if(year % 4 != 0)
{
if(date > 28)
flag = false;
}
if(year % 4 == 0)
{
if(date > 29)
flag = false;
}
}
var dob = new Date();
dob.setFullYear(year, month - 1, date);
var today = new Date();
if(dob > today)
flag = false;
if(flag)
{
$scope.notValidDate = false;
$scope.myForm.$valid = true;
}
else
{
$scope.notValidDate = true;
$scope.myForm.$valid = false;
form.partnerDOBmm.focus();
}
}
}
angular.module('testModule', [])
.directive('onlyDigits', function () {
return {
restrict: 'A',
require: '?ngModel',
link: function (scope, element, attrs, ngModel) {
if (!ngModel) return;
ngModel.$parsers.unshift(function (inputValue) {
var digits = inputValue.replace(/[^\d]/g, "");
ngModel.$viewValue = digits;
ngModel.$render();
return digits;
});
}
};
});
Feel free to improvise the answer, hopefully you can use 'else' in 'validateDOB' function
Realistically, you're better off just using input type="number"
and max
and min
validators, and adding an ng-change
directive that calls a function to update the date.
Since the change isn't fired if the content of the input is invalid, you'll never get a "bad" date:
<input type="number" name="year" ng-model="year" min="2000" ng-change="updateDate()"/>
<input type="number" name="month" ng-model="month" min="1" max="12" ng-change="updateDate()" />
<input type="number" name="day" ng-model="day" min="1" max="31" ng-change="updateDate()" />
Here's a plunk illustrating this solution.
However using text boxes for the month and the day are potentially bad solutions, as it will become a lot more complicated make sure than the day value is kosher. (Think about February and leap year for example). For this whole solution I would recommend using a drop down for the days at the very least, and probably for the month too... as there is a fixed set of results necessary, and you can show or hide the day options based on the value of the month option.
Here's an example of that:
<form name="myForm">
<input type="number" name="year" ng-model="year" min="2000" ng-change="updateDate()"/>
<select name="month" ng-model="month" ng-change="updateDate()">
<option value="1">Jan</option>
<option value="2">Feb</option>
<option value="3">Mar</option>
<option value="4">Apr</option>
<option value="5">May</option>
<option value="6">Jun</option>
<option value="7">Jul</option>
<option value="8">Aug</option>
<option value="9">Sep</option>
<option value="10">Oct</option>
<option value="11">Nov</option>
<option value="12">Dec</option>
</select>
<select name="day" ng-model="day" ng-change="udpateDate()">
<option>1</option>
<option>2</option>
<option>3</option>
<!-- ... SNIP!... -->
<option>27</option>
<option>28</option>
<option ng-show="month != 2 || !(year % 4)")>
29
</option>
<option ng-show="month != 2">
30
</option>
<option ng-show="month == 1 || month == 3 || month == 5 || month == 7 ||
month == 8 || month == 10 || month == 12">
31
</option>
</select>
<p>
{{date | date: 'yyyy-MMM-dd'}}
</p>
</form>
But, but why not dynamically create those selects?
Could you dynamically create the above selects? Sure. Is it worth it? Maybe? Probably not. It only took 30 seconds to type up the options along with the show/hyde logic.
And here's that example in a plunk.
In both of the above scenarios you can just validate on the year validation for 2001:
<span ng-show="myForm.year.$error.min">Must be after January 1, 2001</span>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With