20

I am using the bootstrap datetimepicker in angular 2

https://eonasdan.github.io/bootstrap-datetimepicker/

In my template i have:

<div class='input-group date datepicker'>
         <input type='text' class="form-control" [(ngModel)]="date">
         <span class="input-group-addon">
         <span class="glyphicon glyphicon-calendar" (click)="getCalendar()"></span>
       </span>
   {{date}}
</div>

The problem is when I select a date on the calendar by click, it does not trigger any "change" event (in other words, ngModel is not fired). If i type in the text box, then the {{date}} shows me the value plus the text i typed.

How can I detect changes on the text box if the user clicks on a date in the selector to change it?

Sireini
  • 4,142
  • 12
  • 52
  • 91

5 Answers5

23

Try ngModelChange instead of change.

<input class="form-control" (ngModelChange)="changeDate($event)"
      [(ngModel)]="startRegistrationDate" ngbDatepicker #start="ngbDatepicker">

// to catch the event
changeDate(event: any) {
  console.log(event);
}
Aniruddha Das
  • 20,520
  • 23
  • 96
  • 132
20

The problem is that Angular doesn't know that the input's value has changed in order to update ngModel. Another weird thing is that bootstrap-datetimepicker doesn't trigger the change event on the input - this requires some workaround.

See this working example:

<div class='input-group date datepicker'>
  <input type='text' class="form-control" #datePicker [ngModel]="date" (blur)="date = datePicker.value">
  <span class="input-group-addon">
    <span class="glyphicon glyphicon-calendar" (click)="getCalendar()"></span>
  </span>

  {{date}}
</div>

Notice the changes:

  • [ngModel]="date": we only need one-way data binding (from model to view) since Angular doesn't know when the input's value has changed
  • #datePicker: I've created a local variable in order to access its value
  • (blur)="date = datePicker.value": on blur we update the model
Cosmin Ababei
  • 7,003
  • 2
  • 20
  • 34
7

The problem as stated by the other answers is that ngModel is not picking up the change being made by the third-party library. ngModel uses two-way data-binding. Essentially, when you say

<div [(ngModel)]="date">

that is equivalent to

<div [ngModel]="date" (ngModelChange)="date=$event">.

What I did was create a directive to emit the ngModelChange event, as follows:

@Directive({
    selector: [datepicker],
    outputs: [
        "ngModelChange"
    ]
})
export class DatePickerDirective {
    //Emits changes to ngModel
    ngModelChange = new EventEmitter();
    el: JQuery;

    //Obtains the handle to the jQuery element
    constructor(el){
        this.el = jQuery(el.nativeElement);
    }

    ngOnInit() {
        this.el.datetimepicker({
            //Initialization options
        });

        this.el.on('dp.change', (event) => {
            var moment: Moment = event.date;

            //Emit new value
            this.ngModelChange.emit(date.format(/*However you want it formatted*/));
        });
    };

    ngOnDestroy() {
        //Clean up
        this.el.data('DateTimePicker').destroy();
    };
};
  • 1
    ElementRef doesn't have "on". It was probably removed in rc5 or so. Should this now be `this.el.nativeElement.onchange= function(event) { }`? https://angular.io/docs/js/latest/api/core/index/ElementRef-class.html – Stephen Nov 04 '16 at 18:25
  • should have been `this.el.nativeElement.onchange= (event) => { }` as that fixes the `this` issues – Stephen Nov 04 '16 at 19:29
  • Good to know. Changed this.el to JQuery type (need JQuery typings to do so, unless you're bold enough to use 'any') which is a better representation of this.el anyways. – Joe aka user3169887 Nov 11 '16 at 20:32
  • (ngModelChange) instead of (change) was 'zactly what I needed. – Ron Newcomb Jul 20 '17 at 15:39
5

The solution posted by user3169887 worked great for me until I switched from a template-driven form (uses ngModel) to a reactive form.

Looking further into the original problem, I learned that Angular is listening for the "input" event which these datepicker libraries don't fire. My solution was to tweak the directive to fire the "input" event rather than emit the ngModelChange event. I tested this directive with both a template-driven form and a reactive form and both seemed to work.

import { Directive, ElementRef, Renderer, Input, OnInit } from "@angular/core";

declare var $: any;

@Directive({
    selector: "[datepicker]"
})
export class DatePickerDirective implements OnInit {
    @Input("datepicker")
    private datepickerOptions: any = {};
    private $el: any;

    constructor(private el: ElementRef, private renderer: Renderer) {
        this.$el = $(el.nativeElement);
    }

    ngOnInit() {
        // Initialize the date picker
        this.$el.datepicker(this.datepickerOptions)
            // Angular is watching for the "input" event which is not fired when choosing
            // a date within the datepicker, so watch for the "changeDate" event and manually
            // fire the "input" event so that Angular picks up the change
            // See: angular/modules/@angular/forms/src/directives/default_value_accessor.ts
            .on("changeDate", (event) => {
                let inputEvent = new Event("input", { bubbles: true });
                this.renderer.invokeElementMethod(this.el.nativeElement, "dispatchEvent", [inputEvent]);
            });
    };

    ngOnDestroy() {
        this.$el.datepicker("destroy");
    };
};

Note that I'm using a different datepicker library, so I'm listening for the "changeDate" event and your library may fire a different event like "dp.change".

jaggedaz
  • 128
  • 1
  • 3
0

Not sure if this works but according to the docs the right event to listen for is change

<div class='input-group date datepicker' (change)="date=$event">
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • {{date}} Like this it is not working.. – Sireini Mar 18 '16 at 16:37
  • Can you please try `
    ` and add a `handleChange($event) { console.log(event); }`. I also had a mistake in my answer - updated. (change `$value` to `$event`)
    – Günter Zöchbauer Mar 18 '16 at 16:39
  • 2
    The problem is that bootstrap datetimepicker doesn't trigger the change event on the input. In fact, it has a custom event "dp.change" but angular can't seem to catch it in (dp.change). – Cosmin Ababei Mar 18 '16 at 21:16