UPDATE: IT HAS BEEN CONFIRMED THAT THIS IS AN ANGULAR BUG
Check: https://github.com/angular/angular.js/issues/14734#issuecomment-224558516
The case is, I am trying to create a datepicker field that detects if the input is an invalid date(The months and days validity is also included like 2015-02-30 is invalid because there is no February 30) and is also required. So, I created a custom form validity via directive to detect the invalid date. The custom validity is working fine, however, seems like the $error.required and $invalid does not sync since I have used "ngModel.$validators.available".
Here is a working plunker: http://plnkr.co/edit/NuDGB64IetpcsaVB03T7?p=preview
You will notice in plunker that the $error.required is true but the $invalid is Just blank but turns to true when you input an invalid date and false if the date input is valid. In short, the form is not considered invalid when the input is blank which is very needed for my ng-class
The code is:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/flick/jquery-ui.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script type="text/javascript" src="https://code.jquery.com/ui/1.11.2/jquery-ui.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
</head>
<body>
<form ng-controller="myCtrl" name="bioManagementForm" novalidate>
Degree: <br />
<p>Invalid Input: -- {{ bioManagementForm.in_company_since.$invalid }}</p>
<p>Required -- {{ bioManagementForm.in_company_since.$error.required }}</p>
<input class="form-control input-form since" ng-class="{ 'error': bioManagementForm.in_company_since.$invalid && notifications }" type="text" id="in_company_since" name="in_company_since" ng-model="input.in_company_since" ng-required="true" datepicker>
</form>
</body>
<script>
// Code goes here
var myApp = angular.module('myApp', []);
myApp.controller('myCtrl', function($scope){
});
myApp.directive('datepicker', function() {
return {
restrict: 'A',
require : 'ngModel',
link : function (scope, element, attrs, ngModel) {
console.log(ngModel);
datePatternCounter = [];
// $(function(){
if(!scope.viewOnly){
element.attr("placeholder", "YYYY-MM-DD");
}
element.datepicker({
changeYear: true,
changeMonth: true,
yearRange: "1900:+50",
showButtonPanel: true,
closeText: 'Clear',
dateFormat:'yy-mm-dd',
onSelect:function (date) {
ngModel.$setViewValue(date);
scope.$apply();
},
// Custom function that clears the date value of the model when the "Clear" button is clicked on the datepicker
onClose: function(date){
var event = arguments.callee.caller.caller.arguments[0];
if(Number(date) && date.length == 8){
// alert("dean");
year = date.slice(0, 4);
month = date.slice(4, 6);
day = date.slice(6, 8);
newDate = year+"-"+month+"-"+day;
ngModel.$setViewValue(newDate);
ngModel.$render();
}else{
// alert("armada");
// Code if with dash
// Do nothing
}
// ngModel.$error.datePattern
if(event['type'] == 'click'){
ngModel.$setViewValue(undefined);
ngModel.$render();
}
},
beforeShow: function (e, t) {
id = document.querySelector( '#ui-datepicker-div' );
angular.element(id).addClass('HideTodayButton');
if(element.hasClass('birth_date')){
element.datepicker("option", "maxDate", 0);
}
},
});
// });
// START Syntax to check if the date is valid or not in real time
ngModel.$validators.available = function (modelValue, viewValue) {
if(modelValue){
var check = modelValue.split('-');
var y = parseInt(check[0], 10);
var m = parseInt(check[1], 10);
var d = parseInt(check[2], 10);
var date = new Date(y,m-1,d);
if (date.getFullYear() == y && date.getMonth() + 1 == m && date.getDate() == d) {
check = true;
} else {
check = false;
name = ngModel.$name;
if(datePatternCounter.indexOf(name) == -1){
datePatternCounter.push(name);
element.parent().siblings("span.errors").append('<span class="invalid-date" ng-if="'+ngModel.$error.available+'"> * Invalid Date <br /></span>');
}
}
}
return check;
}
// END Syntax to check if the date is valid or not
}
}
});
</script>
</html>
Change your date validator to return true in case of empty value:
ngModel.$validators.available = function (modelValue, viewValue) {
if(modelValue){
var check = modelValue.split('-');
var y = parseInt(check[0], 10);
var m = parseInt(check[1], 10);
var d = parseInt(check[2], 10);
var date = new Date(y,m-1,d);
if (date.getFullYear() == y && date.getMonth() + 1 == m && date.getDate() == d) {
check = true;
} else {
check = false;
name = ngModel.$name;
if(datePatternCounter.indexOf(name) == -1){
datePatternCounter.push(name);
element.parent().siblings("span.errors").append('<span class="invalid-date" ng-if="'+ngModel.$error.available+'"> * Invalid Date <br /></span>');
}
}
return check; // true or false when value is not empty
}
return true; // empty value should be handled by ng-required, we only check format here, so return true
}
Related
`I am working on a date time input inside a web app. The format for date and time is MM/DD/YYYY HH:MM:SS. I have to validate the month, date and year. For example, it should prevent user from entering 13 instead of 12 in the month placeholder.
Below is html code.
<label>Date time:
<input placeholder="__/__/____ __:__:__" data-slots="_">
</label><br>
and the js
<script>
document.addEventListener('DOMContentLoaded', () => {
for (const el of document.querySelectorAll("[placeholder][data-slots]")) {
const pattern = el.getAttribute("placeholder"),
slots = new Set(el.dataset.slots || "_"),
prev = (j => Array.from(pattern, (c, i) => slots.has(c) ? j = i + 1 : j))(0),
first = [...pattern].findIndex(c => slots.has(c)),
accept = new RegExp(el.dataset.accept || "\\d", "g"),
clean = (input) => {
input = input.match(accept) || [];
return Array.from(pattern, c =>
input[0] === c || slots.has(c) ? input.shift() || c : c
);
},
format = () => {
const [i, j] = [el.selectionStart, el.selectionEnd].map(i => {
i = clean(el.value.slice(0, i)).findIndex(c => slots.has(c));
return i < 0 ? prev[prev.length - 1] : back ? prev[i - 1] || first : i;
});
el.value = clean(el.value).join``;
el.setSelectionRange(i, j);
back = false;
};
let back = false;
el.addEventListener("keydown", (e) => back = e.key === "Backspace");
el.addEventListener("input", format);
el.addEventListener("focus", format);
el.addEventListener("blur", () => el.value === pattern && (el.value = ""));
}
});
</script>
Even if its in jquery, its fine but please no External libraries or plugins.
I have tried to prevent it using prevent.default() for keypress event but have reached nowhere.
I just want the user to be prevented from entering wrong date instead of validating after entered.`
You can do this
<input type="datetime-local" required id="date" name="date">
Not sure you wanted this, but you can consider using the datepicker (jQuery + datepicker.js).
add tags:
<script type="text/javascript" src="../Scripts/jQuery/jquery.min.js"></script>
<script type="text/javascript" src="../Scripts/jQuery/bootstrap-datepicker.js"></script>
<link rel="stylesheet" href="../CSS/bootstrap-datepicker.css" type="text/css">
Html:
<input id="myDate" name="myDate" type="text" class="form-control date-input" placeholder="DD MM YYYY HH:MM:SS" readonly />
jQuery:
$(function () {
$('#myDate').datepicker({
//format of your date displayed
format: "M/d/yyyy h: mm tt"
}).on('changeDate', function (e) {
//some code here
});
Sorry if it's not what you were looking for
So I am using the air-datepicker (http://t1m0n.name/air-datepicker/docs/)
to select a month, presetting to 01/03/2018 for example, which fills a hidden field in my input called startdate2 using the altmethod.
What I want is to update enddate2 with startdate2 + 2 months. I've tried using onChange and a few various methods but nothing seems to be working.
<input type="hidden" name="startdate" id="startdate2" value="" class="inputdate" onChange="getDateAhead()" >
<input type="hidden" name="enddate" id="enddate2" value="" class="outputdate" >
<script>
function getDateAhead()
{
var start = document.getElementById('startdate2').value;
var end = start.setMonth(start.getMonth()+2);
document.getElementById('enddate2').value = end;
}
</script>
Just can't seem to get it working??
Please check documentation(http://t1m0n.name/air-datepicker/docs/) for the datepicker you are using. Your concern function is onSelect.
var eventDates = [1, 10, 12, 22],
$picker = $('#custom-cells'),
$content = $('#custom-cells-events'),
sentences = [ … ];
$picker.datepicker({
language: 'en',
onRenderCell: function (date, cellType) {
var currentDate = date.getDate();
// Add extra element, if `eventDates` contains `currentDate`
if (cellType == 'day' && eventDates.indexOf(currentDate) != -1) {
return {
html: currentDate + '<span class="dp-note"></span>'
}
}
},
onSelect: function onSelect(fd, date) {
var title = '', content = ''
// If date with event is selected, show it
if (date && eventDates.indexOf(date.getDate()) != -1) {
title = fd;
content = sentences[Math.floor(Math.random() * eventDates.length)];
}
$('strong', $content).html(title)
$('p', $content).html(content)
}
})
I have a page where adding new record in the database is the main function. the page is working fine after one condition is given. one of the fields that is required to be added is the date field. the page has a dropdown of year date (2017,2018.2019). when the selected year in the dropdown for example is 2017, all input should be the date within 2017. inputted date that is less or greater than 2017 should not be validated and not accepted. this goes the same when the selected year in the dropdwon is 2018 or 2019. accepted input should be the same of the selected year in the dropdown.
I initially used Jquery validate to do the validation.(im just showing the date part in the validation)
here is the html for the dropdown
<div class="col-sm-2" id="y_type" style="text-align:left;">
<select class="" style="width: 100%; display:inline-block;">
#{
DateTime datetime = DateTime.Now;
for (int i = 0; i <= 2; i++)
{
<option>#(datetime.AddYears(+i).ToString("yyyy"))</option>
}
}
</select>
</div>
holiday_date in the id of the textfield on the other hand,
here's the jquery part that does the validation before
$("form").validate({
rules:
{
"holiday_date[]": {
required: true
},
},
},
errorClass: "invalid",
errorPlacement: function (error, element) {
$('#ResultDialog p').html("#hmis_resources.Message.msg_80005");
$('#ResultDialog').modal();
},
submitHandler: function (form) {
ajaxFormSubmit();
}
});
What I initially do is create a function that test or compare the textfield of date and the value of the dropdown. But this does not work either. and i am not sure of its placing
function testdate() {
$(".holidayBody tr").each(function (key, value) {
var holiday_date = $(value).find(".holiday_date").val();
if (typeof holiday_date !== 'undefined') {
var year = holiday_date.substr(6, 4);
var month = holiday_date.substr(3, 2);
var days = holiday_date.substr(0, 2)
holiday_date = year + '/' + month + '/' + days;
var dropdownear = $('#y_type :selected').text();
var result = year == dropdownear;
alert(result)
}
})
}
if there is a simpler way to accomplish this, i would appreciate if you can share.
You should add a custom method for the validator, like in this fiddle:
Validating year from select input.
<script>
$(document).ready(function() {
var $select = $("select");
//I added only the year to the drop down, you should change it to full date.
for (var i = 0; i < 3; i++) {
var year = new Date();
year.setFullYear(year.getFullYear() + i);
$select.append("<option value=" + year.getFullYear() + " >" + year.getFullYear() + " </option>");
}
// add the rule here
$.validator.addMethod("thisYearOnly", function(value, element, arg) {
var selectedYear = value;
var currentDate = new Date();
var currentYear = currentDate.getFullYear();
return currentYear == selectedYear;
}, "Must select this year");
// configure your validation
$("form").validate({
rules: {
mydate: {
thisYearOnly: true
}
}
});
});
</script>
I have two dates to be compared in the following format
the response coming from backend service has following date format
alignFillDate - 2015-06-09
pickUpDate - 2015-05-10
so, front end needs to check the condition is if pickUpDate is less then the alignFillDate, we will increase the alignFillDate by 30 days, i.e, we increment the pickUpDate to next month(30 days from now ) and show different text on view
How, do i achieve this using angular and javascript. ? how does my html and controller needs to changed for this date calculation
You save those date strings as Date objects, do a comparison with vanilla javascript and assign to scope or this.
var alignFillDate = new Date("2015-06-09");
var pickUpDate = new Date("2015-05-10");
if (pickUpDate < alignFillDate) {
alignFillDate = alignFillDate.setDate(alignFillDate.getDate() + 30);
}
$scope.pickUpDate = pickUpDate;
$scope.alignFillDate = alignFillDate;
Here is a plunk that does what you are trying to do http://plnkr.co/edit/Kq7WA1cBcrwDyxDeBFAL?p=info.
You should use an angular filter to achieve this. The filter takes in the object containing both dates, and will return the formatted date.
Here is a filter that performs this operation:
myApp.filter('customDate', function($filter) {
var DATE_FORMAT = 'yyyy-MM-dd';
return function (input) {
var alignFillDate = new Date(input.alignFillDate);
var pickUpDate = new Date(input.pickUpDate);
if ( alignFillDate > pickUpDate) {
alignFillDate.setDate(alignFillDate.getDate() + 30)
alignFillDate = $filter('date')(alignFillDate, DATE_FORMAT);
return alignFillDate + ' this date has been changed';
} else {
return $filter('date')(alignFillDate, DATE_FORMAT);
}
}
});
Here is a working jsFiddle: http://jsfiddle.net/ADukg/6681/
Other way -- doing "from scratch": (Example in AngularJS). The method isAfterDate(), specifically, returns true if the first date is greater than second date.
Below, date_angular.js:
var DateModule = angular.module("dates", []);
DateModule.controller("dates", function($scope){
$scope.filtros = {};
$scope.filtros.data_first = null;
$scope.filtros.data_second = null;
$scope.isAfterDate = function(){
data_first_day = $scope.filtros.data_first.split("/")[0];
data_first_month = $scope.filtros.data_first.split("/")[1];
data_first_year = $scope.filtros.data_first.split("/")[2];
data_second_day = $scope.filtros.data_second.split("/")[0];
data_second_month = $scope.filtros.data_second.split("/")[1];
data_second_year = $scope.filtros.data_second.split("/")[2];
if(data_first_year > data_second_year){
return true;
}else if (data_first_year == data_second_year){
if((data_first_month > data_second_month)){
return true;
}else if ((data_first_month < data_second_month)) {
return false;
}else{
if(data_first_day == data_second_day){
return false;
}else if (data_first_day > data_second_day){
return true;
}else{
return false;
}
}
}else{
return false;
}
}
$scope.submit = function() {
if (this.isAfterDate()){
alert("The first date is grater than the second date");
}else{
$("#form_date").submit();
}
}
});
RelatoriosModule.directive("datepicker", function () {
return {
restrict: "A",
require: "ngModel",
link: function (scope, elem, attrs, ngModelCtrl) {
var updateModel = function (dateText) {
scope.$apply(function () {
ngModelCtrl.$setViewValue(dateText);
});
};
var options = {
dateFormat: "dd/mm/yy",
onSelect: function (dateText) {
updateModel(dateText);
}
};
elem.datepicker(options);
}
}
});
For other comparisons: only adjust the method.
In the form (form.html), if you are using AngularJS, you can add it in your archive. Below, form.html:
<div ng-app="dates" class="section-one">
<div ng-controller="dates" class="section">
<form method="get" action="dates/dates" id="form_date">
<div class="form-container--linha">
<div class="field-3">
<label for="data_first">first date: </label>
<input id="data_first" type="text" name="data_first" ng-model="filtros.data_first" datepicker/>
</div>
<div class="field-3">
<label for="data_second">second date: </label>
<input id="data_second" type="text" name="data_second" ng-model="filtros.data_second" datepicker/>
</div>
</div>
<div class="actions">
<button class="bt-principal" type="button" ng-click="submit()">submit</button>
</div>
<form>
</div>
</div>
I have function that loops every 500ms, and collects date information:
var mlptoday = {};
var timer = setTimeout(today,500);
function today(){
var d = new Date()
mlptoday.date = checkTime(d.getDate()); //output: "27"
mlptoday.year = d.getFullYear(); //output: "2013"
mlptoday.month = checkTime(d.getMonth()+1); //output: "01"
}
function checkTime(i) { if (i<10){i="0" + i} return i }
In a different function, I would like to check if the date the user gives as input is either the same day, or after the given day.
An example input may be: 2013.01.27.
I use this snippet of code to achieve what I want:
var remTime = "2013.01.27"; //user input
var remTimeArray = remTime.split('.') //output: ["2013","01","27"]
if (
!(remTimeArray[0] >= parent.mlptoday.year &&
remTimeArray[1] >= parent.mlptoday.month) ||
!((remTimeArray[1] == parent.mlptoday.month) ? Boolean(remTimeArray[2]*1 >= parent.mlptoday.date) : true)
){
//the input date is in the past
}
As you could probably guess, this does not work. The conditional statement seems to fail me, because if I invert Boolean(...) with an !(...), it will never fire the error, otherwise it always will.
Here's a snippet, where it works at it should:
var mlptoday = {};
var timer = setTimeout(today,500);
function today(){
var d = new Date();
mlptoday.year = d.getFullYear(); //output: "2013"
mlptoday.month = checkTime(d.getMonth()+1); //output: "01"
mlptoday.date = checkTime(d.getDate()); //output: "27"
$('#values').html(JSON.stringify(mlptoday));
}
function checkTime(i) { if (i<10){i="0" + i} return i }
$(document).ready(function(){
$('form').submit(function(e){
e.preventDefault();
var remTime = $('input').val(); //user input
var remTimeArray = remTime.split('.') //output: ["2013","01","27"]
if (
!(remTimeArray[0] >= mlptoday.year &&
remTimeArray[1] >= mlptoday.month) ||
!((remTimeArray[1] == mlptoday.month) ? Boolean(remTimeArray[2]*1 >= mlptoday.date) : true)
){
$('#past').fadeIn('fast').delay(500).fadeOut('fast');
}
})
})
#past { display:none; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<form>
<input type="text" id="input" required autocomplete="off" placeholder="yyyy.mm.dd" pattern="^(19|20)\d\d[.](0[1-9]|1[012])[.](0[1-9]|[12][0-9]|3[01])$" required="" />
<button>Check</button>
</form>
<pre id="values"></pre>
<span id="past">the input date is in the past</span>
I need a better way to do this, and I don't want to use any date picker plugins.
I would compare the dates as integers to avoid complex logic.
var todayConcat = "" + parent.mlptoday.year + parent.mlptoday.month + parent.mlptoday.date;
var remTimeConcat = remTime.replace(/\./g, "");
if (remTimeConcat < todayConcat) {
//the input time is in the past
}
Just make sure the dates and months always have the leading zero.