Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

support for input type date and time - #1650 #2026

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
244 changes: 244 additions & 0 deletions src/ng/directive/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/;
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
var DATE_REGEXP = /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/;
var TIME_REGEXP = /^([0-2]\d)(:)([0-5]\d)(:)?([0-5]\d)?(\.\d+)?$/;

var inputType = {

Expand Down Expand Up @@ -222,6 +224,144 @@ var inputType = {
'url': urlInputType,


/**
* @ngdoc inputType
* @name ng.directive:input.date
*
* @description
* Text input with date validation. Sets the `date` validation error key if the content is not a
* valid date.
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} required Sets `required` validation error key if the value is not entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
* `required` when you want to data-bind to the `required` attribute.
* @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
* minlength.
* @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
* maxlength.
* @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
* RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
* patterns defined as scope expressions.
* @param {string=} ngChange Angular expression to be executed when input changes due to user
* interaction with the input element.
*
* @example
<doc:example>
<doc:source>
<script>
function Ctrl($scope) {
$scope.value = '2012-08-14';
}
</script>
<form name="myForm" ng-controller="Ctrl">
Date: <input type="date" name="input" ng-model="value"
min="2012-08-01" max="2012-08-31" required>
<span class="error" ng-show="myForm.list.$error.required">
Required!</span>
<span class="error" ng-show="myForm.list.$error.date">
Not valid date!</span>
<tt>value = {{value}}</tt><br/>
<tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
<tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
<tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
</form>
</doc:source>
<doc:scenario>
it('should initialize to model', function() {
expect(binding('value')).toEqual('2012-08-14');
expect(binding('myForm.input.$valid')).toEqual('true');
});

it('should be invalid if empty', function() {
input('value').enter('');
expect(binding('value')).toEqual('');
expect(binding('myForm.input.$valid')).toEqual('false');
});

it('should be invalid if over max', function() {
input('value').enter('2012-09-30');
expect(binding('value')).toEqual('');
expect(binding('myForm.input.$valid')).toEqual('false');
});
</doc:scenario>
</doc:example>
*/
'date': dateInputType,


/**
* @ngdoc inputType
* @name ng.directive:input.time
*
* @description
* Text input with time validation. Sets the `time` validation error key if the content is not a
* valid time.
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} required Sets `required` validation error key if the value is not entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
* `required` when you want to data-bind to the `required` attribute.
* @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
* minlength.
* @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
* maxlength.
* @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
* RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
* patterns defined as scope expressions.
* @param {string=} ngChange Angular expression to be executed when input changes due to user
* interaction with the input element.
*
* @example
<doc:example>
<doc:source>
<script>
function Ctrl($scope) {
$scope.value = '20:00';
}
</script>
<form name="myForm" ng-controller="Ctrl">
Time: <input type="time" name="input" ng-model="value"
min="18:00" max="22:00" required>
<span class="error" ng-show="myForm.list.$error.required">
Required!</span>
<span class="error" ng-show="myForm.list.$error.time">
Not valid time!</span>
<tt>value = {{value}}</tt><br/>
<tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
<tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
<tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
</form>
</doc:source>
<doc:scenario>
it('should initialize to model', function() {
expect(binding('value')).toEqual('20:00');
expect(binding('myForm.input.$valid')).toEqual('true');
});

it('should be invalid if empty', function() {
input('value').enter('');
expect(binding('value')).toEqual('');
expect(binding('myForm.input.$valid')).toEqual('false');
});

it('should be invalid if over max', function() {
input('value').enter('23:00');
expect(binding('value')).toEqual('');
expect(binding('myForm.input.$valid')).toEqual('false');
});
</doc:scenario>
</doc:example>
*/
'time': timeInputType,


/**
* @ngdoc inputType
* @name ng.directive:input.email
Expand Down Expand Up @@ -586,6 +726,110 @@ function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
ctrl.$parsers.push(urlValidator);
}

function dateInputType(scope, element, attr, ctrl, $sniffer, $browser) {
textInputType(scope, element, attr, ctrl, $sniffer, $browser);

// as the input event doesn't trigger from the calendar dialog in chrome,
// we add a binding to the change event
element.bind('change', function() {
element.triggerHandler('input');
});

var dateValidator = function(value) {
if (isEmpty(value) || DATE_REGEXP.test(value)) {
ctrl.$setValidity('date', true);
return value === '' ? null : value;
} else {
ctrl.$setValidity('date', false);
return undefined;
}
};

ctrl.$formatters.push(dateValidator);
ctrl.$parsers.push(dateValidator);

if (attr.min) {
var min = Date.parse(attr.min);
var minValidator = function(value) {
if (!isEmpty(value) && Date.parse(value) < min) {
ctrl.$setValidity('min', false);
return undefined;
} else {
ctrl.$setValidity('min', true);
return value;
}
};

ctrl.$parsers.push(minValidator);
ctrl.$formatters.push(minValidator);
}

if (attr.max) {
var max = Date.parse(attr.max);
var maxValidator = function(value) {
if (!isEmpty(value) && Date.parse(value) > max) {
ctrl.$setValidity('max', false);
return undefined;
} else {
ctrl.$setValidity('max', true);
return value;
}
};

ctrl.$parsers.push(maxValidator);
ctrl.$formatters.push(maxValidator);
}
}

function timeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
textInputType(scope, element, attr, ctrl, $sniffer, $browser);

var timeValidator = function(value) {
if (isEmpty(value) || TIME_REGEXP.test(value)) {
ctrl.$setValidity('time', true);
return value === '' ? null : value;
} else {
ctrl.$setValidity('time', false);
return undefined;
}
};

ctrl.$formatters.push(timeValidator);
ctrl.$parsers.push(timeValidator);

if (attr.min) {
var min = Date.parse('2000-01-01T'+attr.min);
var minValidator = function(value) {
if (!isEmpty(value) && Date.parse('2000-01-01T'+value) < min) {
ctrl.$setValidity('min', false);
return undefined;
} else {
ctrl.$setValidity('min', true);
return value;
}
};

ctrl.$parsers.push(minValidator);
ctrl.$formatters.push(minValidator);
}

if (attr.max) {
var max = Date.parse('2000-01-01T'+attr.max);
var maxValidator = function(value) {
if (!isEmpty(value) && Date.parse('2000-01-01T'+value) > max) {
ctrl.$setValidity('max', false);
return undefined;
} else {
ctrl.$setValidity('max', true);
return value;
}
};

ctrl.$parsers.push(maxValidator);
ctrl.$formatters.push(maxValidator);
}
}

function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
textInputType(scope, element, attr, ctrl, $sniffer, $browser);

Expand Down
Loading