juanjinario
juanjinario

Reputation: 745

How pass a callback funcion (with a http request) from a parent component to child component in Angular

I want to pass a function that has an http request from a parent component to a child component, and from the child component execute the request.

My code as the following:

// myService
constructor(private http: HttpClient)
getIds(id) {
    // return of(['a', 'b', 'c', id]); with this works
    return this.http.get(apiUrl)  // with this doesnt work
 }

// parentComponent.ts
callbackFunction = this.myService.getIds;
constructor(private myService: MyService) { }

// parentComponent.html
<child-component [callbackRequest]="callbackFunction"></child-component>

// childComponent.ts
@Input() callbackRequest;
ngOnInit() {
   this.callbackRequest('d');
}

What confuses me the most is that if from the service I return an observable built from of, if it works. When debugging I see that if the call to the service arrives. The error I get is the following: ERROR TypeError: Cannot read property 'get' of undefined

Upvotes: 0

Views: 784

Answers (3)

JanRecker
JanRecker

Reputation: 1847

Is there a specific reason to work with a callback function?

Normaly i would try to make one component "smart" and one "dumb".
In your case the parent is the one which decides WHAT to do (which HTTP request) and the child decides WHEN to do it. And perhaps the child also should show the result.
Therefore i decided in a similar case, that my child will inform the parent when its time to run the request, then the parent runs the request and provides the result to the child.

Yes, that way i need not just one INPUT, but one INPUT and an OUTPUT for my child. On the other side, my child has a cleanly defined INPUT interface. If i just provide a callback method, the call back may return anything.

Also my child has nearly no "logic" in it. All the magic is in the parent. That makes it quite easy to write UnitTests for the child component. And also for the parent its quite easy with just a mocked child.

Yes, JavaScript (and therefore also TypeScript) allow things like .bind(). But in my experience it seems to solve a problem quite fast, but the real effort comes later. Its harder to understand and its harder to debug. Therefore i normaly avoid it.

Just another way to solve a problem. Pick the parts you like and igore the rest :- )

Upvotes: 0

Barremian
Barremian

Reputation: 31115

The meaning of this keyword inside the getIds function is lost when it's reference is passed around using the @Input binding in Angular. It works when you return of([...]) because there is no usage of this. See here for a canonical post on the meaning of this keyword in a callback

There are two solutions

  1. Using bind - see the post from @GabrielSereno
  2. Use arrow functions

Service

public getIds = (id) => {
  return this.http.get(apiUrl);
}

Component

callbackFunction: any;;
constructor(private myService: MyService) { }

ngOnInit() {
  this.callbackFunction = this.myService.getIds;
}

Template

<ng-container *ngIf="callbackFunction">
  <child-component [callbackRequest]="callbackFunction"></child-component>
</ng-container>

Upvotes: 1

Gabriel Sereno
Gabriel Sereno

Reputation: 855

You can do it:

// myService
constructor(private http: HttpClient)
getIds(id) {
    // return of(['a', 'b', 'c', id]); with this works
    return this.http.get(apiUrl)  // with this doesnt work
 }

// parentComponent.ts
callbackFunction = this.myService.getIds.bind(this.myService);
constructor(private myService: MyService) { }

// parentComponent.html
<child-component [callbackRequest]="callbackFunction"></child-component>

// childComponent.ts
@Input() callbackRequest;
ngOnInit() {
   this.callbackRequest('d');
}

.bind(this) is the solution, because the function will be able to use the parent instance (you can see that everything turn undefined when you don't bind it)

Upvotes: 1

Related Questions