Skip to contentSkip to author details

Creating an Aurelia Bootstrap Calendar Of Events - Part 2 Create a custom element

Written by Jeff Tyler

This is part two of a multi-part tutorial on how to build a bootstrap calendar with Aurelia
Aurelia Calendar Part 1

Aurelia has a built in custom element modeled after web components. This will allow us to create a calendar widget that is isolated and reusable.

First we are going to create a folder called elements
Inside of the folder we will create another folder called calendar and a file called index.ts.
Inside of the calendar folder we will create two files
calendar.html and calendar.ts

We will start with the calendar.ts file. This file is the backing class for the custom element.

we'll break down the pieces and explain what it all is doing.

import { customElement, bindable, inject } from "aurelia-framework";  
import * as moment from 'moment';  
import * as $ from 'jQuery'  
@customElement("Calendar")
@inject(Element)
export class Calendar {

}

import tells it what extra classes we are going to need. If it's typescript then we can just name the class. Most things related to Aurelia is found in the aurelia-framework namespace. We are going to be using the customElement, bindable, and inject classes from aurelia-framework.

IF you are calling a pure JavaScript library then you import it by calling import * as (alias) from where you are getting it. We will be using the Javascript libraries from moment.js and jQuery.

@customElement('calendar') let's Aurelia know that this is going to be a customElement and that when we create an calendar element that we mean this.

@inject(Element) @inject is the Aurelia way of doing dependency injection. Anything in there will be automatically injected into the constructor. Element tells Aurelia to inject the actual Dom element into our constructor.

import { customElement, bindable, inject } from "aurelia-framework";  
import * as moment from 'moment';  
import * as $ from 'jQuery'  
@customElement("Calendar")
@inject(Element)
export class Calendar {  
    private today: moment.Moment;

    @bindable
    currentDate: Date;

    private element: Element
    constructor(element: Element) {
        this.today = moment();
        this.currentDate = this.today.toDate();
        this.element = element;
    }
}

today will hold today's date, so that we could style it differently if we so choose. @bindable tells aurelia to expose the property as an attribute and to allow them to bind to it. currentDate is the date that we will use for creating the calendar. It's basically a placeholder for the month and year.

element is the property to hold the Dom element.
constructor(element:Element) is a type script constructor and gives Aurelia a place to inject the Dom element.
inside the constructor we set today to moment() which creates a moment.Moment object for today's date.
It defaults currentDate to today by getting the date from the moment version. It will be overridden if someone binds the current-date attribute.

Here comes all of the code.

import { customElement, bindable, inject } from "aurelia-framework";  
import * as moment from 'moment';  
import * as $ from 'jQuery'  
@customElement("Calendar")
@inject(Element)
export class Calendar {  
    private today: moment.Moment;

    @bindable()
    currentDate: Date;

    element: Element
    constructor(element: Element) {
        this.today = moment();
        this.currentDate = this.today.toDate();
        this.element = element;
    }

    private getDisplayDates(): Array> {
        let dates = new Array>();
        let beginning = moment(this.currentDate).startOf('month').startOf('week');
        let currentMonth = this.currentDate.getMonth();
        for (let r = 0; r < 6; r++) {
            let week = new Array();
            for (let i = 0; i < 7; i++) {
                let date = {
                    dayOfWeek: beginning.format('dddd'),
                    date: beginning.date(),
                    darken: beginning.month() != currentMonth,
                    events: []
                }
//                if ((i * r) % 4 == 0) {
//                    date.events.push({ name: i.toString() //+ '-' + r.toString() + 'event', amount: i * r + 10 })
//                }
                week.push(date);
                beginning.add(1, 'days');
            }
            dates.push(week);
        }
        return dates
    }

    attached() {
        let that = this;
        var test = $(".calendarDay", $(this.element)).on('click', function (event) {
            let clickEvent = new CustomEvent('day-click', {
                detail: { value: event.target },
                bubbles: true
            })
            that.element.dispatchEvent(clickEvent);
        });
    }
}

Here we added two new methods. getDisplayDates and attached.

getDisplayDates creates an array of arrays of objects used to render the calendar.

beginning turns takes current date and gets the Sunday of the week that the 1st of the month falls on. So for instance for the month of August 2017 it would start at July 30 2017 in the US. If your are localized in a country that has a different start of the week then it will start there instead.

it loops through 6 weeks of 7 days creating an object that holds the date(30,31,1...), the day of week (Monday, Tuesday...) and darken which is a flag for whether the date is within the month held by currentDate.
It also holds an events array used to hold the events to display for that day. Eventually this would be populated by an ajax call for the data.

The commented out section basically creates some dummy data to see what it might look like with events. Uncomment it if you want to get an idea.

attached()

Attached is part of the Aurelia life cycle. It is the last chance to act upon an element. Here we are creating a custom event to be bound to when the a particular day is clicked.
let that = this allows us to access the typescript class from withing the jQuery event.

We add a jQuery event onto all of the calendarDays within this element. Then we create a customEvent and dispatch it so that any listeners can act upon it. It passes the Dom element that was clicked to the event.