100

I want to call parent method (deletePhone) in child component in Angular 4. How can I do that properly?

my parent component looks like:

export class ContactInfo implements OnInit {
    phoneForm: FormGroup;
    phones: Phone[];


    constructor(private fb: FormBuilder,
            private userService: UserService) {
    }

    ngOnInit() {
        this.userService.getDataPhones().subscribe(
            phones => {
                this.phones = phones;
            });

        this.phoneForm = this.fb.group({
            phone: ['', [Validators.pattern(PHONE_PATTERN)]]
        });
    }

    deletePhone(phone: Phone) {
        this.userService.deleteUserPhone(phone)
            .subscribe(res => {
                let index = this.phones.indexOf(phone);
                if (index > -1) {
                    this.phones.splice(index, 1);
                }
        });
    }
}
1
  • 2
    Have you considered using a service for this type of functionality? Commented Sep 29, 2018 at 22:43

4 Answers 4

183
import { Output, EventEmitter } from '@angular/core'; 

...

class ChildComponent {
  @Output() someEvent = new EventEmitter<string>();

  callParent(): void {
    this.someEvent.next('somePhone');
  }
}

In ContactInfo's template

<child-component (someEvent)="deletePhone($event)"
Sign up to request clarification or add additional context in comments.

7 Comments

You can add the intended type like "new EventEmitter<boolean>()" :)
For begginers, you need to add "import { Output, EventEmitter } from '@angular/core' "
It's not just beginners, The answer is incomplete. The answer should be edited to include Sergey's addendum.
for no other reason than being thorough and helpful.
I think someEvent.emit() is preferred, and .next() is deprecated. stackoverflow.com/questions/35840576/…
|
27

This worked for me (example from official docs):

https://angular.io/api/core/EventEmitter#examples

Child:

@Component({
  selector: 'zippy',
  template: `
  <div class="zippy">
    <div (click)="toggle()">Toggle</div>
    <div [hidden]="!visible">
      <ng-content></ng-content>
    </div>
 </div>`})
export class Zippy {
  visible: boolean = true;
  @Output() open: EventEmitter<any> = new EventEmitter();
  @Output() close: EventEmitter<any> = new EventEmitter();

  toggle() {
    this.visible = !this.visible;
    if (this.visible) {
      this.open.emit(null); //emit event here
    } else {
      this.close.emit(null);
    }
  }
}

Parent:

<zippy (open)="onOpen($event)" (close)="onClose($event)"></zippy>

Comments

17

I don't like boilerplate code like @Output(). I found another solution, just pass object with any number of anonymous functions

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-parent',
  styleUrls: ['./parent.component.css'],
  template: `
  <app-child [parentApi]="getParentApi()"></app-child>
`,
})
export class ParentComponent implements OnInit {

  getParentApi(): ParentComponentApi {
    return {
      callParentMethod: (name) => {
        this.parentMethod(name)
      }
    }
  }

  constructor() { }

  ngOnInit() {
  }

  parentMethod(name: string) {
    console.log(`Hello ${name} from parent`)
  }
  
}

export interface ParentComponentApi {
  callParentMethod: (string) => void
}

And child:

import { Component, OnInit, Input } from '@angular/core';
import { ParentComponentApi } from '../parent/parent.component';

@Component({
  selector: 'app-child',
  template: `<button (click)="callParent()">call parent</button>`,
  styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {

  @Input() parentApi: ParentComponentApi

  constructor() { }

  callParent() {
    this.parentApi.callParentMethod("child")
  }

  ngOnInit() {

  }

}

I think this is pretty safe to do this way, no?

4 Comments

I like the idea of this. Unfortunately doesn't work in Angular 7. Gives a template parse error.
I prefer this solution, working fine for me in Angular 10
@Output is not boilerplate, if you are considering @Input. They both have different aspects in parent-child relationship. we can not replace them with one another.
i also do prefer this solution, it is easier to understand and implement especially when you function has multiple params. for the @Output way of doing you have to drastically change the way your method is implemented to do the same job
8

It's simpler than you might think. The key is to pass a parent method to a child @Input property. test it online

Parent component

@Component({
  selector: 'my-app',
  template: `
    <h1>Parent:</h1>
    <p>Parent counting: {{this.count}}</p>

    <child-comp [childInc]="this.inc"></child-comp>
  `
})
export class AppComponent {
  name = "I'm Parent";

  count = 0;

  inc = () => {
    this.count++;
  }
}

Child component

@Component({
  selector: 'child-comp',
  template: `
  <h1>Child:</h1> 
  <button (click)="childInc()">Click me!</button>
  `
})
export class ChildComponent  {
  name = "I'm child"

  @Input() childInc: () => void
}

I've used inc = () => {...} notion in parent, which can remember the right this. If you use the inc(){...} notion, then you need to bind parent this as [childInc]="this.inc.bind(this)".

2 Comments

how can send parameter this way to parent component?
@amirkian Try to introduce a new function in ChildComponent like (click)="newFunc()", and in this newFunc() call the childInc() with parameter.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.