Passing function as arguments in Angular

How components can talk back to each other.

piotr szybicki
12 developer labors
3 min readSep 29, 2019

--

This is going to be a short one. In angular (although probably all the modern frameworks React, Vue.js) you can embed quite a complex login in the HTML template. Via the structural directives *ngFor, *ngIf and the third one that no one uses :), we can express quite a complex presentation logic. Let me give you a quick example.

<h4>NgSwitch</h4>
<ng-container *ngIf="currentUser.isAdmin else regularUser" >
<div *ngFor="let person of people"
[ngSwitch]="person.country">
<uk-user *ngSwitchCase="'UK'" [person]="person"></uk-user>
<usa-user *ngSwitchCase="'USA'" [person]="person"></usa-user>
</div>
</ng-container>
<ng-template #regularUser>
<div>
<default-user [person]="currentUser"></default-user>
</div>
</ng-template>
<button (clicked)="perfomActionInChildCompoenets()"></button>

You have to read the code for a minute to understand what is going on. But here goes a real problem. Let’s assume that at the end of this I add a button that when clicked some action has to be performed in every component that is displayed.

Maybe we can leverage the fact that in JavaScript the functions are first-class citizens and can be pass just like variables. Angular fully embrace TypeScript we can declare the contract for those functions and have to compiler enforce adherence to that contract.

Essentially what I'm proposing is the implementation of the callback pattern. So we need a way to register and unregister the function that will be our callback. We can do that in two scenarios. Either Provide the register function or register event depending on the place where the function is physically written and physically called. Confused? Let's go-to examples.

Register Function

With the register function, a function that is going to be called is defined in the child component and executed in the parent.

Child component
Parent component

So as input to a child component, we provide two functions: registerFunction and unregisterFunction. As a way for registering by the child, a function that is going to be called when a parent decides to ‘lunchNukes’. Inside of that function, we simply iterate over all the function that has been registered and calling it one by one, console.log-ing the output. The benefit of that approach that we can take advantage of the lexical context within that function was declared (closure) and use the private variables that reside inside of the component. Also, each component can provide its own implementation. Going back to the if-else example at the beginning we can imagine US citizen and the UK citizen have a different implementation for launching nukes.

Register Event

With the registered event, a function that is going to be called is defined in the parent component and executed in the child. This is a variation on the Observable pattern that I mentioned before but this is a bit simpler.

child component
parent component

Here we can see something unusual. Usually, the EventEmitter is declared with the @Output decorator:). Not in this case. We create EventEmitter in the parent and pass it to the child. Where you have to subscribe and unsubscribe in order to listen. And this is very very important you have to unsubscribe !!!!. Otherwise, even if the component gets removed from the DOM it will still have a strong reference to the parent so it won’t be garbage collected and it will still receive events. Also, the event emitter has to be recreated every time the child component calles complete on it the emitter becomes useless. The rest is straight forward. The event payload is a function that a child component can invoke. To be honest Bahvioral Subject from RxJs could be a better choice here.

Summary

Is this a bit overkill? might be. And it also might be that there are easier ways to solve that problem(Although I did it this way). The point of this article was to remind developers that function in JavaScript can be passed around. And we can take advantage of that. If I have to do what I described using a more standard approach available in Angular my parent and child component would end up hopelessly coupled. With a lot of duplication of state.

--

--

piotr szybicki
12 developer labors

Piotr Szybicki’s, Programmer, Java Developer, ML Entusiast