Angular Datepicker – Exploring Bootstrap (Part 3)

Home / Angular Datepicker – Exploring Bootstrap (Part 3)

Since I’m using a lot of Angular Bootsrap components in my Angular apps, now seemed like a good time to revisit the classic Datepicker. The Angular Bootstrap Datepicker is relatively easy to get up and going, but I did run into a few quirks.


You can visit the demo link above to see all of the basic functionality of the datepicker. The one implementation I focused on was the Datepicker in a popup. This conforms to my general styling and implementation. The one major quirk that I ran into, though, was that the Datepicker popup is not dismissed automatically when clicking on some other DOM element. The behavior exhibited doesn’t conform to older AngularUI components or, pretty much, any other datepicker popup. It is a problem, for example, when you have multiple datepickers on a single view or some other form element immediately beside the datepicker.

Dealing with this actually presents an interesting scenario in Angular. Initially, I wasn’t sure how/if I could remedy this problem. I did start thinking back to my Multiselect Component, though, and how I handle “host” events within it. This provided a bit of an idea. Angular’s templating system has the concept of “template variables” which effectively let one get a handle to any DOM element. Within our own component that is utilizing the template, we use @ViewChild attributes to get a reference to the template variable.

Taking that into account, I could make my template utilize a variable for any given datepicker’s parent container. I called mine “#startContainer” since this is a start-date datepicker. The container will be available within our component TypeScript as an ElementRef. You will notice that Angular Bootstrap utilizes a template variable itself to define the popup. We’ll need to grab a handle to that as well to take advantage of the “API” provided by the Datepicker popup.

<div class="form-group mb-2 mr-sm-2 mb-sm-0" #startContainer>
  <div class="input-group">
    <input class="form-control" placeholder="Start date" name="startDate" [(ngModel)]="startDate" ngbDatepicker #start="ngbDatepicker">
    <button class="input-group-addon btn btn-default" type="button" (click)="start.toggle()">
      <i class="fa fa-calendar" aria-hidden="true"></i>
    </button>
  </div>
</div>

Inside the component, we will define the @ViewChild references and the document click handler (host event).

@Component({
    selector: 'my-app',
    templateUrl: 'templates/app.html',
    changeDetection: ChangeDetectionStrategy.OnPush,
    host: { '(document:click)': 'hostClick($event)' },
})

export class AppComponent implements OnInit {    
    @ViewChild('start') start: any;
    @ViewChild('end') end: any;

    @ViewChild('startContainer') startContainer: ElementRef;
    @ViewChild('endContainer') endContainer: ElementRef;

Our click “hostClick” method is where we perform all of our logic to determine if the datepicker popup should be forced closed by specific click events. We can use the Datepicker popup’s (start ElementRef) “isOpen()” method to determine if the datepicker popup is currently being displayed. If it is, then we can see if the click event was triggered by an element contained within our “startContainer” element. If it isn’t, then we use the API to force the popup to close.

hostClick(event) {
  let startIsOpen: boolean = this.start.isOpen();

  if (startIsOpen) {
      if (this.startContainer && this.startContainer.nativeElement && !this.startContainer.nativeElement.contains(event.target)) {                
          console.log("Not in startDate container - closing.");
          this.start.close();
      }
  }
}

A Plunker is below that shows the default Datepicker behavior followed by what I am calling “managed” pickers. The managed pickers utilize the implementation described above to close when they should. Try clicking the calendar icons in sequence and you’ll see why the it’s advantageous for the popups to close when they are no longer the focus.

Leave a Reply