Angular2: EventEmitter sometimes fires, sometimes not

1.5k Views Asked by At

A Component's EventEmitter does fire in some cases but does not in other cases. Why?

I have a custom Date picker. You can change the date manually (<input>) or use ng2-bootstrap <datepicker> to select conveniently.

I have this template:

<input [(ngModel)]="dateString"
    class="form-control"
    (focus)="showPopup()"
    (change)="inputChange()">
<datepicker class="popup" [ngClass]="{ 'popup-hidden': !showDatepicker }"
    [(ngModel)]="dateModel"
    [showWeeks]="true"
    (ngModelChange)="hidePopup($event)">
</datepicker>

The component with the relevant parts:

export class CustomDatepickerComponent {
    @Input()
    dateModel: Date;
    dateString: string;

    showDatepicker = false;

    @Output()
    dateModelChange: EventEmitter<Date> = new EventEmitter();

    showPopup() {
        this.showDatepicker = true;
    }

    // Called when the date is changed manually
    // It DOES fire the event
    inputChange() {
        let result = moment(this.dateString, 'YYYY-MM-DD');
        this.update(result.toDate());
    }

    // Called through the DatePicker of ng-bootstrap
    // It DOESN'T fire the event
    // However, the showDatepicker binding takes effect (see template)
    hidePopup(event: Date) {
        showDatepicker = false;
        this.update(event);
    }

    update(date: Date) {
        this.dateModel = date;
        this.dateString = moment(date).format('YYYY-MM-DD');
        // This SHOULD fire the event
        // It is called in BOTH cases! 
        // But it fires only when the date is changed through the <input>
        this.dateModelChange.emit(this.dateModel);
    }

I use this datepicker this way:

<custom-datepicker [dateModel]="testDate" (change)="change($event)"></custom-datepicker>

Here's the change() handler function.

    testDate = new Date();
    change($event) { console.info('CHANGE', $event); }

Any ideas?

3

There are 3 best solutions below

0
On

Ok... Now this is somewhat embarassing. This was the usage of this component:

custom-datepicker [dateModel]="testDate" (change)="change($event)"></custom-datepicker>

Which should be changed to:

custom-datepicker [dateModel]="testDate" (dateModelChange)="change($event)"></custom-datepicker>

The interesting part is that it seems that the changed event of the <input> element bubbled up from CustomDatepickerComponent. It was handled inside the component, it fired dateModelChange which was not handled, then it bubbled up to the outer component as change event -- and this way it was handled.

If I passed the event to inputChange() and called event.stopPropagation() then it was cancelled and did not propagate.

Once again: it was nothing to do with EventEmitter.

0
On

I had a similiar issue. I solved it by decoupling the @ainput date model (datemodel) from the date model used inside the datepicker ( localdate: Date;) Then I used ngOnChange to keep these both variables synced. This way, the EventEmitter is working fine and sending a value for each date selection or update.

export class myDatepicker implements OnChanges{
  localdate: Date;

  @Input() datemodel: Date;
  @Input() isOpen: boolean = false;
  @Input() label: string;
  @Input() disabled: boolean = false;


  @Output() datemodelChange: EventEmitter<Date> = new EventEmitter<Date>();

  // Date has been selected in Datepicker, emit event
  public selectedDate(value: any) {

    this.datemodelChange.emit(value);
}

// React on changes in the connected input property
ngOnChanges(changes: { [propName: string]: SimpleChange }) {

    try {
        if (changes["datemodel"] && changes["datemodel"].currentValue) {
            this.localdate = moment(changes["datemodel"].currentValue).toDate();                // raw Input value
        }
    } catch (ex) {
        console.log('myDatePicker: ' + ex);
    }
}

}

0
On

Another solution for anyone stumbling across this page.

I had a similar issue which was also not the fault of EventEmitter. :-) Initially it appeared my EventEmitter was intermittently not firing, however the culprit turned out to be the click handler responsible for firing the EventEmitter.

I had this in my html template:

<button mat-mini-fab color="primary">
    <i class="fas fa-pencil-alt" (click)="onClickEdit()"></i>
</button>

and the click handler:

  export class MyClass {
  @Output() edit = new EventEmitter();

  constructor() { }

  onClickEdit() {
    console.log('onClickEdit(), this.edit=' + this.edit);
    this.edit.emit();
  }
}

I added the console.log() to debug the problem, and I noticed onClickEdit() was not fired every time I clicked the fancy edit icon, which explains why the EventEmitter wasn't firing. :-)

I fixed this by moving the (click) attribute to the enclosing button (in hindsight it seems kind of obvious.)

<button mat-mini-fab color="primary" (click)="onClickEdit()">
    <i class="fas fa-pencil-alt"></i>
</button>

Problem solved.