I want to use ng-pattern to further enforce the date format on an input field using Angular UI datepicker. The problem is when I do this it's always showing invalid.
<p class="input-group">
<input type="text" datepicker-popup="MM/dd/yyyy" is-open="opened" close-text="Close"
ng-model="someDate" class="form-control" placeholder="mm/dd/yyyy"
ng-pattern="/^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/" required />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open($event)">
<i class="glyphicon glyphicon-calendar"></i>
</button>
</span>
</p>
If I apply the same ng-pattern to a plain input field (no datepicker), it works as expected.
There seems to be a conflict but I'm not sure what it is. Any ideas?
Update:
Here is a simple plunker to illustrate the issue as requested. After some further digging it does seem to be running the pattern against the underlying Date object. When I use a custom directive that formats the date, however, it runs it against the actual input.
The only documentation I see for ng-pattern is a brief mention here under input. Is there anything else that maybe I'm missing?
As I mentioned in my comment, what is happening is that the datepicker directive is changing the model value from a String to a Date object. When ngPattern tried to validate the Date object it will fail because the string value of a Date is not going to match the pattern that you are using.
What you can do is create your own directive that hooks into the $parsers of the ngModelController to run your pattern check and then call $setValidity() based on what the value is. $parsers is actually built for this type of functionality where you want to run your own custom validation on a ngModel value.
Following these bullets is a directive that will accomplish the functionality that you want. I want to explain the logic in the directive before I show you the code:
In order to add your own $parser you have to add a require for ngModel in your directive definition so that you can access the ngModelController
Because RegExp expects the pattern argument without the beginning and trailing slash, I removed them in the pattern directive argument
datepicker changes the model value from a String to a Date. Since datepicker uses unshift to add it's $parser function to the beginning of the $parsers array, we also need to use unshift to put our $parser before datepicker's $parser.
As you mentioned, the datepicker directive will take any value that it can parse into a date and use that for the model. In order to make sure that only dates which match the pattern are used, I'm returning undefined (as specified in the docs) for dates that don't match the pattern. I'm not doing this check for values that come in as a Date object since that means that it was chosen using the datepicker widget.
As I just alluded to in the previous bullet, I'm not doing the validity check if the value is already a Date object since that means that it was chosen using the datepicker widget and that means that it was a valid date.
directive code
app.directive('awDatepickerPattern',function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope,elem,attrs,ngModelCtrl) {
var dRegex = new RegExp(attrs.awDatepickerPattern);
ngModelCtrl.$parsers.unshift(function(value) {
if (typeof value === 'string') {
var isValid = dRegex.test(value);
ngModelCtrl.$setValidity('date',isValid);
if (!isValid) {
return undefined;
}
}
return value;
});
}
};
});
html - make sure that you don't forget to remove the beginning and trailing slash in the regex definition:
aw-datepicker-pattern="^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$"
And here is an updated plunker with my code.
So why again didn't ng-pattern work?
In case you didn't notice the root cause for why ng-pattern doesn't work when used with the datepicker directive is because the datepicker directive adds it's own ngModelController $parser to the beginning of the $parsers array by using unshift, which changes the model value from a String to a Date.
It seems that an alternative solution to the other workarounds posted here is to upgrade to AngularJS 1.4.5, released 2015-08-28:
https://github.com/angular/angular.js/blob/master/CHANGELOG.md#145-permanent-internship-2015-08-28
The above directive plunker only works when the date is required. I fixed it by modifying the directive as follows:
ngModelCtrl.$setValidity('datep',isValid);
Since the date picker runs after this directive it sets 'date' back to valid.
Related
I have the following code:
<input class="form-control"
uib-datepicker-popup="dd.MM.yyyy"
ng-readonly="!isEditable"
datepicker-append-to-body="true"
ng-model="logicalDepotVo.rtCreationDate"
is-open="isDatepickerOpen"
placeholder="Enter Creation Date"
ng-click="isEditable ? onCreationDateDatepickerClick() : ''"/>
The value does not view value of model, but bind model works as ng-model. When I removed uib-datepicker-popup, all works fine. I tried to remove custom format, play with other attributes, but have no any successful result. Maybe, somebody has an idea what should be done?
Probably this could explain your issue (check you ng-model for correct date format):
ng-model - The date object. Must be a Javascript Date object. You may use the uibDateParser service to assist in string-to-object conversion.
angularjs bootstrap
If you read following Angularjs validations, you understand that:
Message will appear if user interacted and did not fill the date manually.
The problem is when date is filled using the datepicker the input is not recognized by Angularjs and still consider $invalid true, so the message remains there which is confusing/problem although date is already filled using datepicker!
<div class="form-group" ng-class="{ 'has-error' : AddForm.Birthdate.$invalid && !AddForm.Birthdate.$pristine }">
<input type="text" required data-provide="datepicker" class="form-control" name="Birthdate" ng-model="Birthdate" />
<span ng-show="AddForm.Birthdate.$invalid && !AddForm.Birthdate.$pristine" class="help-block" >
Birthdate is required.
</span>
</div>
You can either validate it prior to form submit, or else hook a listener on your datepicker to manually set the model property Birthdate value.
It seems bootstrap datepicker is built on top of JQuery datepicker, manually setting the value would be a bad practice you can refer to:
Update Angular model after setting input value with jQuery
a better approach would be to use some built-in angular component such as the ones from:
https://angular-ui.github.io/bootstrap/
http://dalelotts.github.io/angular-bootstrap-datetimepicker/
https://github.com/dalelotts/angular-bootstrap-datetimepicker
I discovered a new way for this problem-
First of all create an id for that input box and then create a function say $scope.assign(), which simply assign the id value to the model of that input.
Something Like this-
$scope.assign = function() {
$scope.modelValue = $('#idName').val();
}
Now use ng-bind="assign()" to your input box.
It worked for me :)
Was facing the issue, and its because of the picker you are using is built on top of Jquery which remains undetectable by the scope on update.
For my new project I have added another library and its pretty awesome.
See the documentation http://dalelotts.github.io/angular-bootstrap-datetimepicker
Providing the piece of code for which I have added a wrapper directive
My Previous Answer was based on work around and because at that time of answer I was pretty new to the angular and now instead of that I will recommend, not to use an library which is built on top of Jquery in Angular project. Instead prefer angular libraries.
Coming on the topic-
For date time picker I found one very good library
https://github.com/indrimuska/angular-moment-picker
You can find more libraries in built in angular, but I found it pretty useful for other validations too like min-date, max-date validation.
Using this library will solve the issue of validation for sure and its pure Angular way.
I'm trying to make a simple input that validates only by matching some data in scope, literally:
<ng-form name="fooForm">
<input required
pattern="/{{foo}}/"
ng-model="dummy"
name="shouldBeFoo" />
<button ng-class="{disabled: fooForm.shouldBeFoo.$invalid}">
Run Foo
</button>
</ng-form>
However, running this code shows that the $invalid attribute doesn't account for the interpolated pattern. Is there a programmatic way of setting the input's validation to equal foo, precisely? Maybe some function predicate?
See ng-pattern to specify a pattern using an expression: https://docs.angularjs.org/api/ng/directive/input
From 1st view seems like data-ng-click can pass some data as argument to method should be invoked during pressing on button.
But I don't see the difference.
I have followed snippets of code:
HTML
<input
type="button"
value="Fess"
ng-click="toggle(2)">
OR
<input
type="button"
value="Fess"
data-ng-click="toggle(2)">
JS
$scope.toggle = function (valueS) {
alert(valueS);
}
Both work.
Thanks,
They are the same thing. You can use data-ng-click to make a valid html.
From the angular docs on directives:
Directives have camel cased names such as ngBind. The directive can be
invoked by translating the camel case name into snake case with these
special characters :, -, or _. Optionally the directive can be
prefixed with x-, or data- to make it HTML validator compliant. Here
is a list of some of the possible directive names: ng:bind, ng-bind,
ng_bind, x-ng-bind and data-ng-bind.
Leaving them out is totally fine for practical purposes. It's just that if you run it through an html validator service, it will not pass as complliant.
HTML5 has an ability to embed custom data attributes on all HTML elements
These new custom data attributes consist of two parts:
Attribute Name
The data attribute name must be at least one character long and must be prefixed with 'data-'. It should not contain any uppercase letters.
Attribute Value
The attribute value can be any string.
<li data-spacing="30cm" data-sowing-time="February to March">Celery</li>
source : http://html5doctor.com/html5-custom-data-attributes/
Found a difference while using with and without Form.
When my element is in a Form they act the same.
When I use data-ng-click on an element not within a form, Click event is not happening.
I have a very simple form where a radio button is required to be selected in order for a form to be valid. The radio buttons are generated by ngRepeat.
As you can see from this fiddle, while the desired behavior is that when the radio button is clicked for the first time, that should validate the form (being the only element), however notice that it takes an additional click (on the same radio button or any other) to validate the form:
http://jsfiddle.net/Xsk5X/3/
What am I missing?
All the other solutions are work-arounds: All you have to do is remove the name attribute, when you use the ng-model attribute you don't need it and they conflict.
Specifying the name causes Angular to get confused because it changes the value once for the angular model and another time for the form element name.
I had this problem because a colleague had copied the radio buttons in the same page and hidden them for temporary reference, so duplicate radio inputs with the same name
Try adding the ng-click attribute to your radio button input.
Credit to Manny D for noting this first. Yes, this is a little hackish, but it works. E.g.,
<input type="radio"
name="groupName"
ng-model="editObject.Property"
ng-value="someValue"
ng-click />
The reason why this is breaking - is because you're setting all radio boxes to be required. As a result, depending on how you write it - angularjs is saying it's invalid because not all have been selected at some point.
The way around this is to do something like the following:
Using checkboxes and required with AngularJS
(check the 1st and 2nd answers). This will resolve your problem.
Seems like an AngularJS 1.0.3 $scope.$apply update problem.
Tested your exact Fiddle in 1.0.2 (check it out yourself) and it works the way you expect it too.
It doesn't seem like there's anything wrong with your code, just that $scope.$apply(or $digest) isn't working as expected on the first select.
A very simple fix is to force the $scope to also update on every select, try changing the following line of code in your form:
<p>Favorite Beatle</p>
change it too:
<p>Favorite Beatle: {{name}}</p>
And you will see how myForm.$invalid is updated even after the first click.
I would try it out with the latest AngularJs version and let us know if that happens there too.
Another solution I can think of it setting the default selected radio, which will cause myForm.$invalid to be false from the beginning. you can do this by adding the following line in your controller code:
$scope.name = "John";
or any default name you want.
Some times the $digest cycle dosen't $apply(fn) because you have two o more instances.
For fix this you need $apply this trick manually, so put this in your directives:
angular.('myApp',[])
.directive('ngRadioExtend', ['$rootScope', function($rootScope){
return {
require: 'ngModel',
restrict: 'A',
link: function(scope, iElm, iAttrs, controller) {
iElm.bind('click', function(){
$rootScope.$$phase || $rootScope.$apply()
});
}
};
}])
and use it as:
<input type="radio" name="input_name" ng-model="some" value="F" ng-required="true" ng-radio-extend>
<input type="radio" name="input_name" ng-model="some" value="M" ng-required="true" ng-radio-extend>
DONE it's the correct way!
The problem of the scope not getting updated still occurs in 1.1.5
A simple work around is to just add
<span ng-show="false"> {{name}} </span>
Fiddle: http://jsfiddle.net/jonyschak/xaQJH/
For IONIC v1,
add name="" to prevent ionic auto-generate attribute name.
Then, I can change the selected item with only one click.
<ion-radio class="label-ticket"
ng-repeat="topic in vm.listTopic track by $index"
ng-value="topic"
ng-model="vm.topicSupport"
name="">
{{ topic.title }}
</ion-radio>