A sweet, stylish and fast datepicker for Angular. Developed for developers who want full control over the stylesheet and patterns. Can be used within a formgroup or as a standalone input with a standalone callback output.
This application requires Angular version 15.1.0 or newer to work correctly.
npm i bm-datepicker
Import module in app.module.ts
:
import { BmDatepickerModule } from 'bm-datepicker';
imports: [
BmDatepickerModule,
...
],
v.1.0.2 - 1.0.4: (Deprected)
v.1.0.1
import { BmDatepickerModule } from "bm-datepicker";
<bm-datepicker></bm-datepicker>
Yes it is. Use formGroupInput
to connect to your formgroup and formControlNameInput
to set a control name.
<form [formGroup]="generatedFormGroup" (submit)="submitForm()">
<bm-datepicker [formGroupInput]="generatedFormGroup" formControlNameInput="dateFrom"> </bm-datepicker>
</form>
If you need to pre-define a date, you can do it in the component.
this.generatedFormGroup = this.formBuilder.group({
dateFrom: ["24-02-2023"],
});
Note! If you don't need Formbuilder at all, then remove both formGroupInput
and formControlNameInput
, otherwise you will get an error.
You will then need the calendarOutput
to fetch the callback event:
<bm-datepicker (calendarOutput)="calendarToOutput($event)"></bm-datepicker>
Of course. Put you validation requirements in you component or service. Bookmaker will handle it via the formControlNameInput
you gave it.
You can add the error messages below the Bookmaker and reference to the input field via the FormControllName. In component:
this.generatedFormGroup = this.formBuilder.group({
dateFrom: ["", Validators.required],
});
In HTML:
<p *ngIf="generatedFormGroup.get('dateFrom')?.errors?.['required']">This field is required</p>
There is a build in invalid
handler, that can be used as an evaluator connected to the entire form (if the input field is in a formgroup). This handler will be activated when the date don't follow the selected pattern
.
<button type="submit" [disabled]="generatedFormGroup.invalid">Submit form</button>
Use the errorMessage
option to show an error message of your choice, underneath the input field, when the date don't follow the selected pattern
.
<bm-datepicker errorMessage="The field is invalid"></bm-datepicker>
Yes, you can! Use the pattern
option to change format. The pattern is using lowercase letters for year, month and day.
<bm-datepicker pattern="mm/dd/yy"></bm-datepicker>
The default format is yyyy-mm-dd
and do not need the pattern to be written out.
The available patterns are... Have it your way! yyyy.mm-dd, dd/yyyy mm, mm/dd-yy - everything goes! The restrictions are
- The dividers can only be space
.
, forward slash/
or dash-
. - Days and months must two letters (mm) and (dd).
- Year can be either two letters (yy) or four letters (yyyy).
Yes, you can!
The Bookmaker will accept changes as long as it follows the pattern
.
You can either pick a date from the calendar or fill it in manually.
The formated date will be presented in the input field and can be used via selected formControlNameInput
or used from the callback calendarOutput
.
Yes. You can lock the field with the [readonly]
option (and it must be inside square brackets []
). This option must have value of true
to work. Default value is always false
and do not need this option.
<bm-datepicker [readonly]="true"></bm-datepicker>
You can use calendarOutput
to hook up to a response function of your own. The response value is the same as selected pattern. Default pattern is yyyy-mm-dd
.
Make a function in the same component as the Bookmaker to fetch the event value from the calendarOutput
.
<bm-datepicker (calendarOutput)="calendarToOutput($event)"></bm-datepicker>
There is an option to fetch an ISO-standard formatted date like Sat Feb 11 2023 01:00:00 GMT+0100
. Use the isoCalendarOutput
in those cases.
Read more about handling data below "Date handling, double calendars and advanced callbacks"
<bm-datepicker (isoCalendarOutput)="isoOutput($event)"></bm-datepicker>
Use the label
option to change the text.
<bm-datepicker label="Date from"></bm-datepicker>
Use the placeholder
option to change the text. The default value is "Pick a date".
<bm-datepicker placeholder="Pick a date"></bm-datepicker>
Bookmaker comes with a default array of the names in english. Use it to change it to a language of your choice. Connect it to weekdays
option.
In the HTML:
<bm-datepicker [weekdays]="weekdays"></bm-datepicker>
In the component:
weekdays = ['Mon', 'Tue', 'Wed', 'Thur', 'Fri', 'Sat', 'Sun'];
Yes! Use the [isSunday]
option (and it must be inside square brackets []
).
<bm-datepicker [isSunday]="true"></bm-datepicker>
Bookmaker comes with a default array of the names in english. Use it to change it to a language of your choice. Connect it to months
option.
In the HTML:
<bm-datepicker [months]="months"></bm-datepicker>
In the component:
months = [
'January', 'February', 'March', 'April',
'May', 'June', 'July', 'August',
'September', 'October', 'November', 'December'
];
You can't change the toggle effect, but you change when it happens. You can use the [manualClose]
option to have the calendar open until you click on the open/close icon.
<bm-datepicker [manualClose]="true"></bm-datepicker>
Note! The toggle effect will occure after manually changes in the input field. This is needed to update the calender correctly and can't be turned off.
Sorry, not yet. Officially. (Maybe if you hack the stylesheet - if so - let me know about it) :-)
I want a different calendar button icon. How do I change that (even if the default icon is quite fancy)?
Bookmaker have a default calendar icon. If you want to use your own, you can add it inside the Bookmaker.
The default size i maximum 20px, but if you want bigger button, you should also change the size in the .bm-toggle-button
style
<bm-datepicker>
<ng-icon name="akarCalendar" size="16px" color="white"></ng-icon>
</bm-datepicker>
You can change everything in Bookmaker. That's the beauty. It is a list of styles, but hey, you are a developer, aren't you? You can use the stylesheet to change parts or everything, it's up to you. Treat it as an usual stylesheet in SCSS. This is an overview of all elements, but you can concentrate it and put togther elements as you wish.
Use styleSheet
option to do the changes, and formControlNameInput
to make an individual stylesheet:
In the HTML:
<bm-datepicker formControlNameInput="dateFrom" [styleSheet]="styles"> <bm-datepicker></bm-datepicker></bm-datepicker>
In the component:
styles = `
p.bm-paragraph{
font-family:"Poppins", Verdana, sans-serif;
color: #000;
margin:0;
padding:0;
}
input.bm-date-input{
font-family:"Poppins", Verdana, sans-serif;
color: #000;
margin:0;
display:flex;
flex:1;
height:40px;
border-radius: 9999px;
padding: 3px 0 3px 20px;
border: 1px solid rgb(195, 195, 195)
}
label.bm-label{
font-family:"Poppins", Verdana, sans-serif;
color: #000;
margin:0;
padding:0;
font-size: .9rem;
font-weight: 500;
margin-left:15px;
}
.bm-date-input-wrapper{
position:relative;
width: 100%;
display:flex;
flex-direction:row;
align-items: center;
}
.bm-date-input-wrapper input[readonly] {
cursor: default !important;
background: rgb(245,245,245);
}
.bm-toggle-button{
position:absolute;
right:5px;
width:30px;
height:30px;
display:flex;
align-items:center;
justify-content:center;
background-color:rgb(0, 153, 235);
border: 1px solid rgb(0, 153, 235);
border-radius: 9999px;
transition:.5s
}
.bm-toggle-button:hover{
background-color: rgb(0, 131, 202);
cursor:pointer;
color:fff;
border: 1px solid rgb(0, 131, 202)
}
.bm-table{
width:100%;
background-color:#f5f5f5;
overflow: hidden;
padding:16px;
display:flex;
flex-direction: column;
border-radius: 8px;
box-shadow: 0 0 0.125rem 0 rgba(0,0,0,0.08), 0 0.125rem 0.75rem 0 rgba(0,0,0,0.24);
box-sizing:border-box;
}
.bm-tr{
display:flex;
flex:1;
justify-content:space-between;
align-items:center;
flex-direction: row;
}
.bm-th{
display:flex;
flex:1;
justify-content:center;
align-items:center;
flex-direction:row;
}
.bm-td{
display:flex;
flex:1;
justify-content:center;
align-items: center;
}
.bm-td-empty{
display:flex;
flex:1;
justify-content:center;
align-items: center;
}
.bm-td-empty .bm-td-inner-empty{
height:30px;
width:30px;
margin:2px;
}
.bm-td-inner{
height:30px;
width:30px;
margin:2px;
display:flex;
justify-content:center;
align-items: center;
border-radius:9999px;
transition: .4s;
border: 1px solid #f5f5f5;
}
.bm-td-inner:hover{
background-color: rgb(0, 131, 202);
cursor:pointer;
border: 1px solid rgb(0, 131, 202)
}
.bm-td-selected-day{
background-color: rgb(0, 153, 235);
cursor:pointer;
border: 1px solid rgb(0, 153, 235)
}
.bm-td-inner:hover p{color:#fff !important}
.bm-td-selected-day p{color:#fff !important}
.bm-th p{
font-size: .9rem;
font-weight: 500;
}
.bm-td-inner p{
font-size: .8rem;
font-weight: 300;
}
.bm-daylabels-wrapper{margin-bottom: 20px}
.bm-year-month-title {
display:flex;
flex: 5;
justify-content: center;
align-items: center;
flex-direction: column;
margin-bottom: 10px
}
.bm-year-month-title p.bm-month-title{
font-size: 1.5rem;
font-weight: 600;
margin-top: -8px
}
.bm-td-current-day{border: 1px solid rgb(0, 202, 101)}
.bm-td-lock-day{
pointer-events:none;
background: repeating-linear-gradient(-55deg,rgb(200, 200, 200), rgb(200, 200, 200) 2px,rgba(0,0,0,0) 2px, rgba(0,0,0,0) 4px);
border: 1px solid rgb(200, 200, 200)
}
.bm-arrow {
border: solid #000;
border-width: 0 3px 3px 0;
display: inline-block;
padding: 3px;
}
.bm-td-inner:hover .bm-arrow{
border-color:white;
border-width: 0 3px 3px 0;
}
.bm-arrow-right {transform: rotate(-45deg);}
.bm-arrow-left {transform: rotate(135deg);}
.bm-weekend {color: #ff0000 !important}
.bm-error-message {
padding: 8px 16px 20px 16px;
}
.bm-error-message p{
color: #ff0000;
font-size: .8rem;
}
`;
To handle external fonts in Bookmaker, you need to have them globally in your Angular project. Use the link below in your root index.html
to get Poppins fontstyle:
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700;800&display=swap" rel="stylesheet" />
The default behavior is to lock dates before current date. Current day is indicated with a green border. Selected date have blue background (if you don't change the stylesheet...)
<bm-datepicker></bm-datepicker>
You must use pattern
and selectedDate
options. Default pattern is yyyy-mm-dd
. If you're using this, you don't need to add this option, only selectedDate
.
It is important that selectedDate
uses the same pattern as pattern
.
<bm-datepicker pattern="dd-mm-yyyy" selectedDate="24-02-2023"></bm-datepicker>
If you want a dynamic date from the component, you can wrap the option name inside square brackets []
selectedDateValue="24-02-2023"
And add square brackets around the option [selectedDate]
.
<bm-datepicker pattern="dd-mm-yyyy" [selectedDate]="selectedDateValue"></bm-datepicker>
Note! The option selectedDate
overrides the default settings. If only selectedDate
is used without lockDateBefore
or lockDateAfter
, all dates are unlocked.
If you want to define your own lockings you can use the lockDateBefore
or lockDateAfter
options or use [lockDateBefore]
or [lockDateAfter]
for dynamic values.
It is important that lockDateBefore
and lockDateAfter
uses the same pattern as pattern
.
<bm-datepicker pattern="dd-mm-yyyy" lockDateBefore="24-02-2023"></bm-datepicker> <bm-datepicker pattern="dd-mm-yyyy" lockDateAfter="24-02-2023"></bm-datepicker>
You can either combine both lockDateBefore
and lockDateAfter
to limit the input range.
<bm-datepicker pattern="dd-mm-yyyy" lockDateBefore="20-02-2023" lockDateAfter="24-02-2023"></bm-datepicker>
Or you can add lockDateAfter
with selectedDate
if you need all dates before the lockDateAfter
.
<bm-datepicker pattern="dd-mm-yyyy" selectedDate="24-02-2023" lockDateAfter="24-02-2023"></bm-datepicker>
Or add all three and get a limit input range and a pre-selected date.
<bm-datepicker pattern="dd-mm-yyyy" selectedDate="24-02-2023" lockDateBefore="20-02-2023" lockDateAfter="24-02-2023"></bm-datepicker>
Of course! This is not a tiny lite version! This is the real deal! I assume that you have read through the documentation, because this tutorial needs some basic knownledge.
In this tutorial you will learn how to use two datepickers - a "From Date" and a "To Date".
The "From Date" will be responsable for locking lockDateBefore
inside "To Date" and the "To Date" will be responsable for locking lockDateAfter
inside "From Date".
Why?
Because it will prevent "From Date" to be selected later than "To Date" and vice versa.
Also - "From Date" will be locked before current date.
It is important to use square brackets []
though the values will be dynamic.
There will also need the callback function calendarOutput
that will be used in the component.
<bm-datepicker label="From Date" pattern="dd-mm-yyyy" [lockDateAfter]="lockDateAfterValue" [selectedDate]="selectedFromDateValue" (calendarOutput)="lockDateBefore($event)"></bm-datepicker> <bm-datepicker label="To Date" pattern="dd-mm-yyyy" [lockDateBefore]="lockDateBeforeValue" [selectedDate]="selectedToDateValue" (calendarOutput)="lockDateAfter($event)"></bm-datepicker>
In the component we need variables to hold the dates and functions to receive callbacks from the datepicker.
lockDateAfterValue: string = ""
lockDateBeforeValue: string = ""
selectedFromDateValue: string = ""
selectedToDateValue: string = ""
lockDateBefore(event:any){
this.lockDateBeforeValue = event.selectedDate
}
lockDateAfter(event:any){
this.lockDateAfterValue = event.selectedDate
}
You can also pre-define dates from, for example, a database. Use the variables in the component to set values:
lockDateAfterValue: string = "24-02-2023";
lockDateBeforeValue: string = "15-02-2023";
selectedFromDateValue: string = "24-02-2023";
selectedToDateValue: string = "20-02-2023";
So this works fine. But what about limit the before date to current date in both calendars?
The "To Date" calendar is just fine, as it is, but if we use the dynamic option of [lockDateBefore]
inside the "From Date"...
<bm-datepicker label="From Date" pattern="dd-mm-yyyy" [lockDateAfter]="lockDateAfterValue" [lockDateBefore]="lockDateBeforeValue" [selectedDate]="selectedFromDateValue" (calendarOutput)="lockDateBefore($event)"></bm-datepicker>
...There will only be trouble. The problem is that the value will change everytime everytime we select a new date. We can always select forward, but never backwards. How to solve this - it is simple. Add a new variable in the component that won't be updated at all:
constantLockDateBeforeValue: string = "23-02-2023";
And add it to the [lockDateBefore]
in "From Date":
<bm-datepicker label="From Date" pattern="dd-mm-yyyy" [lockDateBefore]="constantLockDateBeforeValue" [lockDateAfter]="lockDateAfterValue" [selectedDate]="selectedFromDateValue" (calendarOutput)="lockDateBefore($event)"></bm-datepicker> <bm-datepicker label="To Date" pattern="dd-mm-yyyy" [lockDateBefore]="lockDateBeforeValue" [selectedDate]="selectedToDateValue" (calendarOutput)="lockDateAfter($event)"></bm-datepicker>
Done!
That's it! Now it's up to you! Good luck!
Steffo Dimfelt steffo.dimfelt@gmail.com
- 1.0.10: Adjust stylesheet
- 1.0.8: Adjust locked days and selected date