What is MultipleDatePicker ?
MultipleDatePicker is an Angular directive to show a simple calendar allowing user to select multiple dates with a bunch of options to customize it.
Get it
NPM style
npm install multiple-date-picker --save
Then you can require it and add to your angular app
var mdp = require('multipleDatePicker');
angular.module('yourApp', [..., mdp]);
Bower style
bower install multiple-date-picker --save
Hipster style
git clone https://github.com/arca-computing/MultipleDatePicker.git
Time to use it
Include at least css file.
<link rel="stylesheet" type="text/css" href="multipleDatePicker.css"/>
<script type="text/javascript" src="multipleDatePicker.min.js"></script>
If don't use something that gets dependencies for you, don't forget to include dependencies manually : Angular and MomentJS.
Add it to your angular app
var app = angular.module('myApp', ['multipleDatePicker']);
Add it in your html
<div>
<multiple-date-picker></multiple-date-picker>
</div>
It will use the max width of it's parent, so If you want to reduce it specify a width (or use Bootstrap/Foundation).
<div style="width:50%">
<multiple-date-picker></multiple-date-picker>
</div>
Inject momentJS
Take a moment for MomentJS
You have 2 ways to inject momentJS. You do nothing and use the global variable or you pass it to the directive :
$scope.moment = require('moment');
<multiple-date-picker moment="moment" />
Change locale
You can change MomentJS locale, the datepicker is listening to this changement and will update days and month labels
Test it, it'll change locale for whole demo page : EN - FR
moment.locale('fr-FR');
Options
ngModel (for the watch)
Major change since previous version, we now use a ngModel attribute. Bind your array of moment dates, watch it to get notified when a date is selected/unselected (and please don't forget the true option at the end of the $watch)
$scope.myArrayOfDates = [moment().date(4), moment().date(5), moment().date(8)];
$scope.$watch('myArrayOfDates', function(newValue, oldValue){
if(newValue){
console.log('my array changed, new size : ' + newValue.length);
}
}, true);
<multiple-date-picker ng-model="myArrayOfDates"></multiple-date-picker>
month
You can change current month when you want.
$scope.myMonth = moment().add(3, 'MONTH');
<multiple-date-picker month="myMonth" />
quick change year
You can use those options to show a select to quickly change year. When you select a year it'll re-render the select to always have the same range around the selected year.
Works with disallow-back-past-months and disallow-go-futur-months.
<multiple-date-picker change-year-past="5" change-year-future="10"/>
quick change month
You can use this option to show a select to quickly change the month.
<multiple-date-picker enable-select-month="true"/>
First day of week
By default Monday will be the first day of week, but you can set calendar to use Sunday as first day of week
<multiple-date-picker sunday-first-day="true" />
Show previous/next month days
You can show previous and next months days with this option. You can combine all 3 options to fit your needs.
- showDaysOfSurroundingMonths : if true empty boxes will be filled, default is false
- cssDaysOfSurroundingMonths : applied css class to empty boxes. You can chain like 'myclass1 myclass2 myclass3', default is 'picker-empty'
- fireEventsForDaysOfSurroundingMonths : fire events on empty boxes, default is false
<multiple-date-picker
show-days-of-surrounding-months="true"
fire-events-for-days-of-surrounding-months="true"
css-days-of-surrounding-months="'picker-other-month-custom'"
highlight-days="highlightDays"
/>
We use custom (and pretty ugly I know) css to customize previous/next months dates and highlight days
.picker-day.picker-other-month-custom{
background-color: #d17357;
color: #fff;
}
.picker-day.picker-other-month-custom:hover{
cursor: pointer;
background-color: #ae5d4a;
color: #fff;
}
.picker-day.picker-other-month-custom.picker-selected{
background-color: #9c5244;
}
.picker-day.holiday{
background-color: #00ffa2;
color: #fff;
}
.picker-day.holiday.picker-selected{
background-color: #00ae69;
color: #fff;
}
.picker-day.holiday.picker-off{
background-color: #00a267;
color: #fff;
cursor: default;
}
.picker-day.holiday:not(.picker-off):hover{
background-color: #00e795;
color: #fff;
}
.picker-day.birthday:not(.picker-selected){
background-color: #ded100;
color: #000;
}
dayClick/dayHover
You can use those callbacks to check something and prevent the event to happen (click will select/unselect date, hover will trigger hover event on the date).
$scope.checkSelection = function(event, date) {
if(somethingWrongWithThisDate(date)){
event.preventDefault();
console.log('my click was prevented, want am I gonna do with my life ?');
}
}
<multiple-date-picker day-click="checkSelection" day-hover="anotherCallbackIfYouNeed"></multiple-date-picker>
right-click
Right click callback
<multiple-date-picker right-click="rightClickCb"/>
all-days-off
Disable all dates in the calendar, default is false
<multiple-date-picker all-days-off="true"/>
days-allowed
Enable only some dates, it's an array of moment dates
<multiple-date-picker days-allowed="daysAllowed"/>
Highlight-days
This is an array containing dates you want to highlight, I can be a day-off like a bank day, holiday, your birthday, anything you want.
{
date: Timestamp/String/Moment, //anything usable with moment
css: String //class applied, can be "class1" or "class1 class2 class3 ..."
selectable: true/false,
title: String //will be use in title attribute (show when cursor is hover)
}
The live demo represent the 2nd of month as holiday (not clickable), 14h a day off (not clickable) and the 25th is my birthday (not really, and clickable).
$scope.highlightDays = [
{date: moment().date(2).valueOf(), css: 'holiday', selectable: false,title: 'Holiday time !'},
{date: moment().date(14).valueOf(), css: 'off', selectable: false,title: 'We don\'t work today'},
{date: moment().date(25).valueOf(), css: 'birthday', selectable: true,title: 'I\'m thir... i\'m 28, seriously, I mean ...'}
];
<multiple-date-picker highlight-days="highlightDays"/>
And here the css used for this demo
/*apply colors if not selected, if selected I want the default selected css*/
.picker-day.holiday:not(.picker-selected){
background-color: #00ffa2;
color: #fff;
}
/*if my holiday isn't clickable (it's the case in the demo)*/
.picker-day.holiday.picker-off{
background-color: #00a267;
color: #fff;
cursor: default;
}
/*my birthday, is clickable*/
.picker-day.birthday:not(.picker-selected){
background-color: #ded100;
color: #000;
}
Week-days-off
Those are days for every week that are not selectable. Sunday = 0, Monday = 1 ... Saturday = 6.
<multiple-date-picker week-days-off="[0, 3]"/>
disable-days-before
If filled will disable all days before this one (not included)
$scope.today = moment();
<multiple-date-picker disable-days-before="today"/>
disable-days-after
If filled will disable all days after this one (not included)
$scope.today = moment();
<multiple-date-picker disable-days-after="today"/>
month-changed
The callback will be called everytime you change month (previous or next month). Be aware that newMonth and oldMonth given to the callback is first day of month at midnight.
This exemple shows how to pass a scope function that will prompt an alert with infos.
$scope.logMonthChanged = function(newMonth, oldMonth){
alert('new month : ' + newMonth.format('YYYY-M-DD') + ' || old month : ' + oldMonth.format('YYYY-M-DD'));
};
<multiple-date-picker month-changed="logMonthChanged"/>
month-click
The callback will be called everytime you change month (previous or next month). You can prevent the event to happen. Be aware that newMonth and oldMonth given to the callback is first day of month at midnight.
This example prevent navigation more than 6 month around now.
$scope.disable6MonthsFromNow = function(event, month){
if(month.isBefore(moment().subtract(6, 'month'), 'month') || month.isAfter(moment().add(6, 'month'), 'month')){
event.preventDefault();
}
};
<multiple-date-picker month-click="disable6MonthsFromNow"/>
disable-navigation
If true the back button to go previous month will be disabled if you are on today's month, so you can't go in past month
<multiple-date-picker disable-navigation="true"/>
disallow-back-past-months
If true the back button to go previous month will be disabled if you are on today's month, so you can't go in past month
<multiple-date-picker disallow-back-past-months="true"/>
disallow-go-futur-months
If true the back button to go next month will be disabled if you are on today's month, so you can't go the futur
<multiple-date-picker disallow-go-futur-months="true"/>
Examples
One day selection only
You want user to be able to select only one day, that's not a problem. Try to select multiple days, you'll not be able. Yeah we call it MAGIC !
$scope.selectedDays3 = [];
$scope.oneDaySelectionOnly = function (event, date) {
$scope.selectedDays3.length = 0;
};
<multiple-date-picker calendar-id="'myId2'" ng-model="selectedDays3" day-click="oneDaySelectionOnly"/>
Let's get serious
You can (of course) use all options at the same time.
Dependencies
The calendar uses 2 dependencies you must add to your project : angular of course and moment.
Because we use moment.js, you can load a language file and change the calendar language (days and months names), like this :
moment.lang('fr');
var app = angular.module...
Week days order, week days names and month + year format cannot be changed with an option.
What's next ?
We created this directive to have a simple calendar with multi-dates selection. We will keep it simple but any improvement is welcome.