How to call @Input EventEmitter from parent in Angular 2

23.1k Views Asked by At

Hi I have a child component with the following @input:

@Input() inputEvents: EventEmitter<{ type: string, data: string | DateModel }>;

this.inputEvents.subscribe((e: any) => {
        if (e.type === 'action') {
          if (e.data === 'toggle') {
            this.toggle();
          }
          if (e.data === 'close') {
            this.close();
          }
          if (e.data === 'open') {
            this.open();
          }
        }
}

How do I trigger the subscribe from the parent? I have tried the following in the parent component but it does not work:

@Output() datePickerAction: EventEmitter<{ type: string, data: string }>;

this.datePickerAction.next({ type: 'action', data: 'toggle' });
3

There are 3 best solutions below

0
On

Angular suggests to use the ngOnChange() event for that:

OnChanges
Angular calls its ngOnChanges method whenever it detects changes to input properties of the component (or directive). This example monitors the OnChanges hook.The ngOnChanges method takes an object that maps each changed property name to a SimpleChange object holding the current and previous property values.

https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#onchanges

So in your case, you could just "subscribe", like this:

export class MyComponent implements OnChanges {

    @Input('parentSelection') private parentSelection;

    ngOnChanges(changes: SimpleChanges) {

        let newVal = changes['parentSelection'].currentValue;


        console.log('parent changed value to: ' + newVal);

        this.someFunction();
    }

    // depending on your needs you could also do *ngIf toggles in the template
    private someFunction() {

        switch (this.parentSelection) {

            case 'toggle':
                this.toggle();
                break;

            case 'open':
                this.open();
                break;

            case 'close':
                this.close();
                break;

            default:
                console.log("unknown event detected: " + this.childValue);
                break;
        }

    }

}
0
On

Although ngChanges is a possibility, I don't like the overhead and usual performance penalty. My favourite design pattern to deal with parent-driven events is an input setter:

@Input() set checkValidityTrigger(something: boolean) {
    this.checkVisibility$.next(something);
  }

that triggers a subject

checkVisibility$ = new Subject<boolean>();

to which I subscribe in the onInit method

this.checkVisibility$
      .pipe(...)
      .subscribe();

Edit: Of course, you can also use something like NGXS, dispatch an event and catch it in your children. But for simple and usually form and template-related events, I find the above-described method to work well.

0
On

If you child component has:

@Input x(): number;

Then you are being asked to pass a value from the parent to the child component as so:

<my-component [x]="myVariable"></my-component>

Where 'myVariable' represents a variable that is of type 'number'. In your case, your input is named 'inputEvents' and is of type 'EventEmitter<{ type: string, data: string | DateModel }>'. So from you parent component, you would pass in the input just the same.

<ng2-datepicker [(ngModel)]="myDate" [inputEvents]='toggleCalEvent'></ng2-datepicker>

Where variable 'toggleCalEvent' is of type 'EventEmitter<{ type: string, data: string | DateModel }>', as so on your parent component:

private toggleCalEvent: EventEmitter<{ type: string, data: string | DateModel }> = new EventEmitter();

Then you could call on this EventEmitter like any EventEmitter:

openCal() {
     this.toggleCalEvent.next({ type: 'action', data: 'toggle' });
}

Since the datepicker is subscribed to this 'inputEvents', then when you trigger a 'next', it will trigger this event in the child. But there is still one issue. If you are triggering this event on a click of something, it will not work. Try this:

<div 
    (click)='openCal()' 
    (mouseenter)='openCal()'
    style='background-color:yellow; width:100px; height:50px;'
></div>
<ng2-datepicker [(ngModel)]="myDate" [inputEvents]='toggleCalEvent'></ng2-datepicker>

You will see that the calendar toggles on mouseenter but not click. This is because the datepicker is scripted to close if you click anywhere outside the calendar. So it opens and closes immediately on click of the div. To prevent this, you can add this to prevent the click from bubbling up:

<div 
    (click)='openCal($event)' 
    (mouseenter)='openCal($event)'
    style='background-color:yellow; width:100px; height:50px;'
></div>
<ng2-datepicker [(ngModel)]="myDate" [inputEvents]='toggleCalEvent'></ng2-datepicker>

openCal(e) {
    this.toggleCalEvent.next({ type: 'action', data: 'toggle' });
    e.preventDefault();
    e.stopImmediatePropagation();
}

Now the calendar show toggle as expected.