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.

Recurring event date picker final result
Recurring event date picker final result

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!

Angular Enthusiast