jQuery UI Datepicker with AngularJS
Yesterday I had to implement jQuery’s Datepicker with Angularjs, at first I thought it would be straight forward, just a directive to configure the datepicker and setup the input field, but I found there is more behind it. The calendar was working fine but the model were not being updated with the selected date. So after some research I came up with the solution bellow:
app.directive('datepicker', function() { return { restrict: 'A', require : 'ngModel', link : function (scope, element, attrs, ngModelCtrl) { $(function(){ element.datepicker({ dateFormat:'dd/mm/yy', onSelect:function (date) { scope.$apply(function () { ngModelCtrl.$setViewValue(date); }); } }); }); } } });
The directive is restricted to an attribute and the link() function is used to setup the datepicker. The interesting part in inside the onSelected method, there I am using the ngModelController to update the model using it’s $setViewValue method.
The ngModelController
“NgModelController provides API for the ng-model directive. The controller contains services for data-binding, validation, CSS update, value formatting and parsing. It specifically does not contain any logic which deals with DOM rendering or listening to DOM events. The NgModelController is meant to be extended by other directives where, the directive provides DOM manipulation and the NgModelController provides the data-binding.” – Angular Docs
In my opinion this is the best solutions because you rely only on the ngModel directive for the two-way data bind, there is no need to use $parse or reach the model directly through the scope.
EDIT: As pointed by jsanti, don’t forget to include the scripts in order:
1 – Jquery
2 – Jquery-ui
3 – angularjs.
Otherwise Angular will use JQlite intead of JQuery and the directive will break.
EDIT: As pointed by Craig, wrapp ngModelCtrl.$setViewValue(date) with scope.$apply().
The best answer I found, simple and useful
Excellent work!!
I’ll keep following u
Thanks Osvaldo, glad you liked it! ;)
Thanks for your answer, simple, clean and fast, thank you so much, finally finish my job
Very useful, thanks!!!
would you allow we copy some content from the article? Thanks.
Thanks for your answer:)
Thanks for the solution. I am finding a small difficulty in the above date picker, that is its very hectic to select the old dates. How can we have a dropdown for month and year in our calender. It will be very helpful if you suggest some answers. Thank you in advance :)
Hi Bema ! Excellent example. Thanks for taking the time to develop it!.
I have used it in my project without no problem (when I have only one datepicker in the form) but now I’m facing a problem because I have 2 datepickers and no matter which one of them I select, it will always show the same date for both inputs. Do you have any idea of how this can be solve?
Thanks in advance for the help !
Hi sWat,
To select old dates easily you could display month and year dropdowns inside the calendar, for that you just have to set changeMonth = true and changeYear = true inside datepicker’s configuration object. Have a look at this Plunker.
Best
Hi Andrey,
This is happening because both inputs are pointing to the same model, so when you change one both get updated. The fix is simple, set each input to a different model, this is done in the ng-model attribute of the input.
Have a look at this plunker.
Hope it helps.
Sure Tim, the content of the blog is under Creative Commons (CC BY 3.0)
Actually it helps! hahaha it was really easy ! Thanks man ! I will keep following your blog for sure !
Thanks !
Thanks, you’ve made my day!
How can I send a different date format to the model?
Hi Dave,
You could change the format inside the onSelect method and update the model using ngModelCtrl.$setViewValue(newDate). Here is a plunker.
Very Helpful!. Something important to note when using this directive —> include the scripts in order:
1 – Jquery
2 – Jquery-ui
3 – angularjs.
thank’s! code and fun!
Good point jsanti, otherwise angular will use JQlite instead of Jquery and the directive will break. I’ve included your comment in the post.
Thanks and good coding ;)
i’m an angular newbie but loving it so far.
your example is a bit advanced for me but i managed to get to work on my first angular based web app. thanks great work.
I was wondering if i was possible to integrate it with the angular ui at http://angular-ui.github.io/
Hi Adrian,
If you are using angular-ui maybe it’d be better if you use bootstrap-ui datepicker, this way you won’t depend on JQuery for the datepicker. I really like angular-ui, it is maturing fast and I think it is a good choice for angular apps, but the solution above is a good one if you have an app that already depends on JQuery-ui and you don’t want to include another library.
The best solution, thanks great work. :D
Glad to help ;)
Sir, I am getting an error when the site loads this directive.
element.datepicker is not a function. What will be the possible mistake i would have done??
Hi safiyu,
Have you included the JqueryUI script? I guess you forgot to include it or the JqueryUI you are using is a custom built that doesn’t include datepicker.
Check this JSBin, I have commented out the JQueryUI in the head and it is getting the same error (element.datepicker is not a function).
Thank You!!
This solution has solved a big problem for me. I was going about fixing this all the wrong way. thanks for sharing.
thank you ….
very helpful….
Hey, awesome solution, thanks for sharing!
nice post. thank you!
have you tried to implement it with the icon trigger?
http://jqueryui.com/datepicker/#icon-trigger
Hi cliff, glad you like it. To use the icon you just have to configure the datepicker, the rest stays the same , here is a plunker
thanks again for implementing it. it works very well.
how about if the trigger is a button, which is not part of the date picker? i exploring how to make two directives communicate with each other in Angularjs. sorry for being a pain :)
Hi cliff, there are several ways to make directives communicate to each other, usually I prefer services, but you could also use the scope or the directive’s controller depending on your case.
Have a look at this plunker, I’ve coded two directives, one with an isolated scope and a controller that is responsible for the datepicker, and the other that requires the datepicker’s controller and uses it’s toggle function to show/hide the calendar. Once a date is selected the datepicker directive will broadcast the selected value to it’s child scopes.
Another thing is that the datepicker is being attached to a div element instead of an input.
I think this is more or less what you asked, init? ;)
Hope it is useful.
Best
Hey, element doesn’t have a reference to datepicker. Yes I have a reference to jqueryUI you can see that I can access the method by grabbing the element from the DOM, but accessing by element doesn’t seem to work. HELP?
In the console:
element.datepicker()
TypeError: Object [object Object] has no method ‘datepicker’
$(‘#docExpDate’).datepicker()
[
]
element
[
]
Thanks for the post this is the most helpful example of writing a directive to wrap the jquery ui datepicker I’ve found. Can you help clarify a few thing for me. I actually don’t want a datepicker but a calendar that always shows which should be easy since the jquery ui datepicker allows for this by simply changing the to a with the datepicker directive which I did in this fork of your example.
http://plnkr.co/edit/NI2HZ8H3FMn6fnHir2Rj?p=preview
But the initialization of the date $scope.date = ’19/03/2013′; doesn’t select the correct date in the calendar.
I added a jquery ui stylesheet to your example so the selected item can be seen.
Several examples including this one from an AngularJS book by O’Reilly
https://github.com/shyamseshadri/angularjs-book/blob/master/chapter8/datepicker/datepicker.js
not only call setViewValue but also call scope.$render() in the link function to sync the other direction model to view but this seems to break more stuff in your example than help so I commented it in my example but I guess I’m also unclear on why you didn’t need to call $render in your example. Thanks for any insight in advance.
Great post again. Forgot to mention. Instead of this:
ngModelCtrl.$setViewValue(date);
scope.apply();
Best practice is to call it like this:
scope.$apply(function(){
ngModelCtrl.$setViewValue(date);
});
The best rationale I’ve read about why is at the bottom of this article:
http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
Hi Lanier, sorry about the delay… being busy… to make it easier please set a fiddle or jsbin with your code, otherwise I can just speculate about the problem :)
Hi Craig, I see the problem, when the datepicker is visible it won’t update when the date model changes (I’ve simulated it with a setTimeout).
In this case I think the easiest way is to watch the model and apply any changes using the date picker’s setDate.
ngModelCtrl.$render won’t work because the calendar is controlled by JQuery, meaning it is “outside” Angular.
Here is a plunker
Good point, I’ve updated the post ;).
Best
Very nice article. Thank you!
I’m a beginner in angularjs. say that I want to be able to selecet a range of date, like in this example:
with the user not able to select toDate < fromDate etc… how would you implement that in angular?
Hi yakir,
I think the simplest way is to use the same directive to implement one datepicker for each model (fromDate and toDate), the directive could check if the element has a minDate parameter (populated with fromDate’s value) and we use it to set it’s minDate option, this is done inside datepicker’s beforeShow function. You could use the same approach to set maxDate if you needed. Here is a plunker
Hope it helps
best
Works great…thanks, I’ve been able to use this to make other directives as well.
One question though
how do you use the attrs to pass dynamic values, such as maxDate, et.al.?
thanks
Hi
Your solution works great for all browsers except ie 8 .
Is there a fix for ie 8 it doesnt seem to load angular app .
I tried ur demo link also but same issue
Hi Nitish,
Luckily I don’t support i.e. 8 anymore, neither personally nor at work, I don’t even have it installed so unfortunately I won’t be able to help you. You could take that question to stackoverflow though.
Best
Hi wef,
In a nutshell, Angular normalises all attributes you set in the directives element and makes them available in the attrs object. this means that in case of dynamic values, for every digest cycle the expression will be run and its result bound to the attrs.
Writing "scope.date = date" gives the same result. : : link : function (scope, element, attrs, ngModelCtrl) { $(function(){ element.datepicker({ dateFormat:'dd/mm/yy', onSelect:function (date) { scope.date = date; // ngModelCtrl.$setViewValue(date); <-- NO NEED? scope.$apply(); : : Can anyone explain why $setViewValue is needed?
Is there any way to get this to work within ng-repeat elements?
Hi Platus, $setViewValue is preferred because this way the directive doesn’t have to know the name of the model it has to update, that is why we use ngModelCtrl, so if you have another input that binds to another model, lets say, dateOfBirth, the directive would work without need to update scope.dateOfBirth.
Hey Ryan, I've set a plunker with ngRepeat, the trick is to bind each input to a different model http://plnkr.co/edit/IbxI6VQHGcItAeqJObEq?p=preview