How to create recurring event date picker in Angular
In this article, I will show you how to create a recurring event date picker form based on options input by user.
From the form, user can select the start date and end date, the frequency they want an event will repeat, e.g. daily, weekly or monthly. Then the number of occurrences will be auto calculated, and the event dates will be displayed accordingly.
By the end of this article, you will be able to build a component like this.
Project setup
First, we will create a new Angular project named recurring-event-date-picker
ng new recurring-event-date-picker
cd recurring-event-date-picker
We will use ng-bootstrap library in our project for UI part, but you can choose whatever library you like.
ng add @ng-bootstrap/ng-bootstrap
In order to add recurring date picker functionality, we will use RRule library. According to RRule npm home page:
rrule.js supports recurrence rules as defined in the iCalendar RFC, with a few important differences. It is a partial port of the rrule module from the excellent python-dateutil library. On top of that, it supports parsing and serialization of recurrence rules from and to natural language.
To install RRule library, run the following command
npm install --save rrule
Then, we will start the application to ensure the setting up is done correctly.
ng serve -o
Create a recurring date picker form
In this step, we will build a form to allow user to select the following information
- Start date and end date the recurring will happen.
- Frequency: whether they want to repeat daily, weekly or monthly.
- If users select weekly, they will be able to choose which day in a week the recurring event would be, e.g. every Monday and Friday.
- If users select monthly, there’s an option to choose on which day in a month the recurring event will happen, e.g. every 13th of the month.
We use Angular Reactive form to build the form. The code will look like this.
@Component({
selector: 'app-recurring-event-picker',
templateUrl: './recurring-event-picker.component.html',
styleUrls: ['./recurring-event-picker.component.scss']
})
export class RecurringEventPickerComponent implements OnInit {
recurringForm: FormGroup; private today: NgbDate; constructor(
private fb: FormBuilder,
private calendar: NgbCalendar
) { } ngOnInit(): void {
this.initRecurringForm();
} private initRecurringForm(): void {
this.recurringForm = this.fb.group({
startDate: [this.today, Validators.required],
endDate: [this.calendar.getNext(this.today, 'd', 7), Validators.required],
frequency: [Frequency.DAILY],
onWeekday: this.fb.array(
[false, false, false, false, false, false, false].
map(val => this.fb.control(val))
),
onMonthday: [this.today]
});
}
}
In the above code, the onWeekday
is an array of boolean
value, in the UI, it is mapped as a list of checkboxes starting from Monday to Sunday. If user selects Monday and Friday, we will have an array like this
// user selects Monday and Sunday
onWeekday: [true, false, false, false, false, false, true]
Later, we will use this array to map to another option value, which is used to create RRule
object.
The RRule
object is created by calling the constructor, and it receives an options object.
// create a new RRule object via constructor
const ruleOptions = { ... };
const rule = new RRule(ruleOptions);
Every time the recurring form changes its value, we will listen to changes and create an option object. This object will be passed to RRule object to create a new rule set.
Then, the rule
object has a method to get all the occurrences based on the options we passed in. The method name is all
It will return an array of native Date
objects.
this.recurringForm.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((value) => {
const options: Partial<Options> = {
freq: value.frequency || Frequency.DAILY,
dtstart: toNativeDate(value.startDate || this.today),
until: toNativeDate(value.endDate || this.today),
byweekday: value.frequency === Frequency.WEEKLY ?
this.getWeekday(value.onWeekday) : null,
bymonthday: value.frequency === Frequency.MONTHLY ?
(value.onMonthday && value.onMonthday.day || this.today.day) : null
};
console.log('options', options);
const rule = new RRule(options);
this.dates = rule.all();
});
The toNativeDate
is just a helper function to convert NgbDate
used in Bootstrap to native Date
object.
The getWeekday
is another utility method to convert an array of boolean value to an array of RRule weekday enum. For example if we have an array like this [true, false, false, false, false, false, true]
, then it will map to an array like this [RRule.MON, RRule.SUN]
We’ve built a form in which user can select recurring options to generate dates from these options. Here comes the final result we had.
You can find the full source code of the project at this link.
Thanks for reading and have a great day!