In some very rare cases, you may need to call a child component’s method directly from a parent component. Generally speaking, this should be seen as only a last resort. Component communication in most cases should be limited to data binding (Both input and output), and and in some cases using a service to emit values between two components.

However, there has been times where I’ve had race conditions between two components that can only be solved by a very precise order of methods being called. This means that I need them to happen synchronously. For that, this method is a life saver , and it’s simple too!

Consider I have the following component :

@Component({
  selector: 'app-parent',
  templateUrl: './parent.component.html',
  styleUrls: ['./parent.component.scss']
})
export class ParentComponent implements OnInit {
}

And I also have a child component like so :

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit {
    callMe(value : string) { 
        console.log('Called : ' + value);
    }
}

Inside the view of parent.component.html, I place the child component.

<app-child></app-child>

Now inside my parent component, I can actually use ViewChild like so to get a direct reference to the child.

export class ParentComponent implements OnInit {
    @ViewChild(ChildComponent, {static : true}) child : ChildComponent;
}

Notice that I don’t pass in a “string” to find like we sometimes do with a ViewChild, we pass in the actual type of component we are looking for.

Then, it’s as easy as calling something on our child.

export class ParentComponent implements OnInit {
    @ViewChild(ChildComponent, {static : true}) child : ChildComponent;
	
    callMyChild(){
        child.callMe('Calling from the parent!');
    }
}

The usual ViewChild rules apply however that generally speaking, you only have access to ViewChild references after the view is initialized (So you cannot access them in an ngOnInit method, you have to use ngAfterViewInit).

Again, it’s typically much better to use data binding or a “joining service” for the two components to communicate. But often it can be hard to sync up the precise order of actions that need to happen. So for that, ViewChild is the winner.

Someone entering the world of javascript for the first time may be confused about the 3 different ways to define variables. var, let and const all seem to do roughly the same thing, so which one is actually correct to use? And why do we have all 3 in the first place?!

I wanted to write this guide as a really simple way to understand all 3, without having to read through RFC’s or really get into the nitty gritty as there’s actually a very simple explanation for which ones to use (And the one to not use!).

Pre 2015

You may be interested to know that before 2015, there was only the keyword “var” in javascript to define variables. I do want to add that in some transpilers, such as Coffeescript or early Typescript, there were different ways to define variables that didn’t strictly involve using the keyword “var”. But by the time they were compiled down to javascript, they themselves were using var under the hood anyway.

In 2015, ES6 was released and we were introduced to let and const in plain javascript. What I mean by “plain” javascript is that using the keyword let or const in a browser without something like typescript/coffeescript or a transpiler like Babel was a-ok.

Var Pitfalls

So now that we know let and const were introduced in 2015, the question becomes why they were even needed? Well let’s look at some examples.

var myVariable = "ABC";
var myVariable = 123;

Any beginner programmer will likely look at the above and think “Well, you’ve re-used the same variable twice, that should be an error. Then no less, the first time you used it, it was a string, then the second time an integer, so double errors please!”. And you would be wrong.

Var in Javascript allows you to re-use the same variable name twice, without an issue. On top of this, it allows you to assign different types to the same variable without a hitch. The latter is maybe not so much of a problem if you’re coming from something like PHP that allows similar problems, but the former is a big problem.

Let’s look at another problem.

myVariable = 5;
console.log(myVariable);
var myVariable;

You may think that this code is going to have big problems. I’m trying to use a variable before I’ve even declared it. But again, Javascript needs to be special with a feature called “hoisting”. What it means is that a javascript variable declaration is always “hoisted” to the top of it’s scope and can be used before it’s even been declared. This can lead to very unintended consequences and confusing behaviour.

As an example :

var myVariable = 5;
if(myVariable == 1)
{
    var myVariable = 2;
}
console.log(myVariable);

What will this example print? You may think it will print 5. As the initial declaration sets myVariable to 5, then only if it’s 1 will it enter the next block of code. But again, because Javascript hoists the inner declaration, this example will actually print 2.

Another example may look like so :

for (var i = 0; i < 5; i++){
  console.log(i);
}
console.log(i); //We might expect this to errors!

What we would expect in most other languages is that the second log would error because the variable declaration is within the loop. Incorrect! Hoisting again pulls the declaration to the top of the scope.

What you’ll find is that the mix of hoisting and being able to override existing declarations in Javascript without issue can often lead to very difficult to find bugs in code.

Using let

And of course, that’s where the keyword “let” comes in.

This now becomes an error :

let myVariable = "ABC";
let myVariable = 123;

But it’s important to note that this does not error even though we are changing the variable type :

let myVariable = "ABC";
myVariable = 123;

let is also scoped to (generally) be unique within curly braces. As an example :

for (let i = 0; i < 5; i++){
  console.log(i);
}
console.log(i);//Throws an error as I is not defined

I would note that using the let keyword gives you this sort of “optional” cascading affect where you can override in a child scope, without affecting the parent. As an example :

let i =1;
if(i < 5) {
	console.log(i);
}
console.log(i);

This prints “1 1” as the variable i was available inside the child scope.

However this code also has no problem compiling

let i =1;
if(i < 5) {
	let i = 2; //new line
	console.log(i);
}
console.log(i);

This prints “2 1”. Where the second initialization of i is set to 2 and printed, but it doesn’t affect the parent i.

In essence, let gives us plenty of the flexibility of var, but without the hoisting and without being able to re-declare a variable with the same name in the same scope.

Using const

Const has many of the same restrictions as the let keyword, but with a few more added on for good measure.

The const keyword does not allow you to re-assign a value to it, even if it’s the same type.

const myVariable = "ABC";
myVariable = "XYZ";//Error

Because of this, you also can’t define a const variable without assigning a value to it.

let myVariable; //No problem
const myConstVariable;//Error

Our example earlier where we were able to re declare a let variable inside a child scope also doesn’t work with const.

const i =1;
if(i < 5) {
	const i = 2; //error
	console.log(i);
}
console.log(i);

In this case, const is actually available in child scopes also, but it is not able to be “re-declared”.

Const is the most restrictive variable declaration type, but it allows us to be certain that after being declared, the value will never change.

So Which Should I Use?

Generally speaking, you should not be using var unless in extreme circumstances where you actually want hoisting (read : never).

So the choice comes down to let or const. Use const if you want to ensure that the value will never change. In general, it’s worth getting in the habit of using const as your default declaration as 9 times out of 10, the value actually won’t change. Then, only when you run into a roadblock where the value may actually need to be changed, switch the declaration to let.

Requesting “lookup” data from an API in Angular can quickly weigh your application down if you’re having to grab the same data over and over. The type of lookup data I’m talking about is say fixed country lists, or a set of user roles, or any other set of data that is rarely going to change on a daily, weekly, or even monthly basis.

I recently tried to solve this by implementing a level of caching in the Angular app that was purely in memory. I figured that if I cache everything in memory, then a simple refresh of the page “clears” the cache, but requests to grab any lookup data on multiple pages won’t result in multiple calls to my API. I also figured it was going to be the easiest way to approach things.

Avoiding Interceptors

There’s a tendency in Angular that when you are dealing with anything HTTP request related, you create an interceptor for it. I’ve been guilty of this in the past for sure! But the problem with Interceptors is that it’s a bit of an all or nothing approach. Opting out of interceptors is not that easy, and “configuring” when an interceptor runs and when it doesn’t is also a bit of a black box. For example, someone looking at a piece of that looks like so :

this.http.get(`myAPI.com`);

Does this cache your request? Does it run any interceptors at all? It’s hard to say without really digging into the code.

And don’t get me wrong, I do use interceptors a lot. But generally speaking, I try and make it so that in the overwhelming majority of cases, I want the interceptor to run. In our case, we only want to run the caching interceptor on a few select endpoints.

CachedHttpClient Service

Instead what I did is I created a CachedHttpClient that functions more or less like your regular HTTP service you already have in Angular.

The code :

import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';

@Injectable({
    providedIn: 'root'
})
export class CachedHttpClient
{
    cachedItems : any[] = [];

    constructor(private http : HttpClient) { 

    }

    getCached<T>(url : string) : Observable<T> {
        if(this.cachedItems[url])
        {
            return of(this.cachedItems[url] as T);
        }

        return this.http.get<T>(url).pipe(map((item : T) => {
            this.cachedItems[url] = item;
            return item;
        }));
    }
}

All this does is before running your HTTP call, we check if the exact URL is in the cache (This includes query strings also), and if not, run the http call, add the item to the cache, and then return the result. On the next request, it’s going to hit the cache and not have to make the http call!

We can inject it into a service like so :

export class MyService {
  constructor(private http : HttpClient, private cachedHttp : CachedHttpClient) { 
  }

  getLookupData() {
    return this.cachedHttp.getCached<LookupData>(`myAPI/LookupData`);
  }
}

Notice that it’s extremely easy to see what’s cached and what’s not. Not only are we using a class called CachedHttpClient, but the actual method call is “getCached”. There’s no hidden magic going on for a developer to shoot themselves in the foot.

Limitations

It is worth pointing out a few limitations.

If a user refreshes their browser (Or closes their browser), then the cache is lost. In my case, this is acceptable because I only really want to cache values as users move through the site. I don’t mind that when they do a hard refresh the first few pages have to fetch those values again and re-cache them. Infact, I somewhat like the fact that a user doesn’t have to know any “tricks” on clearing their “browser cache”, they just have to refresh.

And finally, the above code does not really handle race conditions all too well (e.g. If on a page, two components request the same lookup data). To me again, this was acceptable. Introducing some sort of lock mechanism was really overkill and in 99% of cases, you aren’t going to run into an issue where on one page, you request the same data at exactly the same time (Or you shouldn’t anyway!).

If you’re a C# developer moving to javascript, one of the very first things you’ll miss is the ability to write Linq on lists. While Javascript has a couple of methods to deal with arrays, some of them don’t quite match up to the power of Linq. Luckily, there is a great javascript library called Lodash which has almost everything you need! If you are interested in adding Lodash to your Angular project, you can read our guide here :¬†https://tutorialsforangular.com/2020/08/02/using-lodash-with-angular/

I’m going to do this page cheatsheet style and simply list out the Linq method with it’s Lodash equivalent. Feel free to bookmark this page and come back to it when you start scratching your head thinking “I know how to do this in C#…” as quite often, it’s just a simple rename of your method and you are good to go.

My Models

For the sake of simplicity, the model I will be using will look like so :

{
    people : [
        {
            "firstName" : "John"
            "lastName" : "Smith"
            "age" : 30
        }
    ]
}

Lodash / C# Linq Cheatsheet

All

//C#
items.All(x => x.Age == 30);
//Lodash
_.every(items, x => x.age === 30);
OR
_.every(items, {'age': 30});

Any

//C#
items.Any(x => x.Age == 30);
//Lodash
_.some(items, x => x.age === 30);
OR
_.some(items, {'age': 30});

Average

//C#
items.Average(x => x.Age == 30);
//Lodash
_.meanBy(items, x => x.age === 30);
OR
_.meanBy(items, 'age');

Contains

//C#
items.Contains(myItem);
//Lodash
_.includes(items, myItem);

Count

Count how many match a predicate, not just the length of the array. Note that the return object for Lodash is essentially a group by on the predicate. For example with a collection with two items, one with an age of 30 and the other of an age of 31. The result is :

{
  false: 1,
  true: 1
}
//C#
items.Count(x => x.Age == 30);
//Lodash
_.countBy(items, x => x.age === 30);
OR
_.countBy(items, {'age' : 30});

Distinct

Note that in different versions of Lodash, the functions unique, uniq, and uniqBy have all been swapped around so you may need to try a variant of those depending on your lodash version.

//C#
items.Distinct(x => x.FirstName);
//Lodash
_.unique(items, x => x.firstName);
OR
_.unique(items, 'firstName')

First / FirstOrDefault

Lodash doesn’t provide functionality to either throw an exception like the C# First, or to return null like FirstOrDefault. Instead it will always return an empty array ([])

//C#
items.First(x => x.Age == 30);
//Lodash
_.first(items, x => x.age === 30);
OR
_.first(items, {'age': 30})

Foreach

//C#
items.ForEach(x => x.Age = 30);
//Lodash
_.forEach(items, x => x.age = 30);

Last / LastOrDefault

Similar to First / FirstOrDefault, Lodash doesn’t provide functionality to either throw an exception like the C# Last, or to return null like LastOrDefault. Instead it will always return an empty array ([])

//C#
items.Last(x => x.Age == 30);
//Lodash
_.last(items, x => x.age === 30);
OR
_.last(items, {'age': 30})

Max

Max in C# linq returns only the value of the property (e.g. If the max age is 30, it will return the value 30, not the actual array item). Lodash however will return the entire item that has the highest property value. If you want the entire item in C#, you have to use OrderBy/OrderByDescending.

//C#
items.Max(x => x.Age);
//Lodash
_.max(items, x => x.age);
OR
_.max(items, 'age'})

Min

Similar to Max, C# will return only the property while Lodash will return the entire item.

//C#
items.Min(x => x.Age);
//Lodash
_.min(items, x => x.age);
OR
_.min(items, 'age'})

OrderBy / OrderByDescending

Some versions of Linq only support sortBy which only sorts in ascending value. Similar to C#, orderby is stable, that is, the original order of the items is kept in tact and you must read the resulting object.

//C#
var sortedItems = items.OrderBy(x => x.Age);
OR
var sortedItems = items.OrderByDescending(x => x.Age);
//Lodash
var sortedItems = _.orderBy(items, ['age']);
OR
var sortedItems = _.orderBy(items,['age'], ['desc']);

Select

//C#
items.Select(x => x.FirstName);
//Lodash
_.map(items, x => x.firstName)

Skip

//C#
items.Skip(10);
//Lodash
_.slice(items, 10)

Sum

//C#
items.Sum(x => x.Age);
//Lodash
_.sumBy(items, x => x.age);
OR
_.sumBy(items, 'age');

Take

//C#
items.Take(10);
//Lodash
_.slice(items, 0, 10)

Where

//C#
items.Where(x => x.Age == 30)
//Lodash
_.filter(items, x => x.age === 30);

One painful thing when working with Angular is getting used to Observables vs Promises, and how some libraries use one or the other exclusively. It can be incredibly frustrating to add a library only to find it wants to force you into using promises, when the rest of your project uses observables.

But why would you use one or the other? Well…

You generally use an Observable if :

  • You are building something entirely for Angular as most of Angular uses Observables, it’s nice to keep to the same script.
  • You need to output more than one value over time/stream data, observables allow you to output multiple values while a promise can only output one.
  • You want to use RxJS operators such as map, tap etc.

You generally use a Promise if :

  • You are only ever going to return a single value
  • You prefer the “await” construct over using “then” or “subscribe”

In will get a feel over time as to which one you will need to use, but if you are using Angular, then chances are you will be sticking with Observables.

The real challenge comes when you have a library that will only return promises/observables, and your code is written completely the other way. Well never fear!

Turning Promises Into Observables

Converting a promise into an observable is rather easy with the “from” operator.

First, import “from” from the rxjs library :

import { from } from 'rxjs';

Then it’s as easy as :

from(myService.myPromiseMethod()).subscribe(x => console.log(x));

Be very careful that you use the “from” operator and not “of”. For example :

of(myService.myPromiseMethod())

This will return an observable but the value will be the promise itself, not the returned value of the promise. of is a really useful operator to return synchronous values inside your code, but should not be used for “unwrapping” promises.

Turning Observables Into Promises

If you thought going from a promise to an observable was easy, converting an observable into a promise is actually extremely easy with the “toPromise()” method available on every observable.

let myValue = await myService.getAll().toPromise();

There really isn’t much magic to it at all!

On almost every single Angular project after a certain size, you’ll start running into these errors :

An unhandled exception occurred: Call retries were exceeded

Or

JavaScript heap out of memory

Both of these point to the same issue. That your build process is hitting memory limits and simply crashing. It can be frustrating to debug for a few reasons.

  • Memory limits can be set for all NodeJS processes on a machine, meaning that what will build on one developers machine may not build on another because they may have already upped/lowered their memory limits.
  • Similar to the above, certain hosted build agents (e.g. Azure Devops, Github Actions etc), may have memory limits set lower/higher than you might think, meaning that builds succeed/fail here when they wouldn’t otherwise on other machines.
  • Non production builds of your angular project (e.g. Just using ng serve) build fine, but building with the production flag consumes far more memory, and so may crash only when building in production.

Luckily there is a really easy fix that gets around all of the above. While you can leave a note in the readme saying “Hey, you need to up your memory to XYZ”, a better option is to create a npm build script that sets the memory limit for that build only, and leaves everything else on the machine as is.

For example, a recent project of mine has these sets of scripts in the package.json :

"scripts": {
  "ng": "ng",
  "start": "ng serve",
  "build": "ng build",
  "test": "ng test",
  "lint": "ng lint",
  "e2e": "ng e2e",
  "build-prod": "node --max_old_space_size=8000 ./node_modules/@angular/cli/bin/ng build --prod",
}

Notice the last script. It looks complicated but it’s actually quite simple.

  • Start the Node process.
  • Set the “max old space size” to 8000MB.
  • Find the angular CLI in your node modules folder.
  • Run an angular build of production.

Now anytime we need to build for production, we only need to run “npm run build-prod”, including on our build servers.

As to what you should set your memory limit to. You should try and set it relatively high, but not enough to consume all of your memory on your machine. By default, depending on your installed version of node, the default is between 500MB to 1GB. So even allowing up to 2 or 4GB should be enough to build most Angular projects.

Moving between projects that use NPM and Yarn typically isn’t a big deal. For the most part, the package dependency managers work almost identical. But one thing that does tend to trip developers up is the subtle command line changes between the two. Often it’s just the case of swapping install/uninstall in NPM to add/remove in Yarn, so here’s a quick cheatsheet for doing just that.

NPM CommandYarn CommandDescription
npm install [package-name]yarn add [package-name]Installs a package
npm install [package-name] –save-devyarn add [package-name] –devInstalls a package as a dev dependency
npm installyarn OR yarn installInstalls all dependencies inside package.json
npm uninstall [package-name]yarn remove [package-name]Uninstalls a package
npm uninstall [package-name] –save-devyarn remove [package-name]Uninstalls a package as a dev dependency (Yarn command is the same as uninstall regular dependency)
npm updateyarn upgradeUpdates all packages
npm update [package-name]yarn upgrade [package-name]Updates a single package
npm install [package-name] -gyarn global add [package-name]Installs a globally accessibly package
npm uninstall [package-name] -gyarn global remove [package-name]Uninstalls a globally accessibly package

While the above are the main commands that have subtle differences. There are actually some commands that are identical between NPM and Yarn, that you basically just sub out the word npm with yarn on the command line and you are good to go. These are :

NPM CommandYarn CommandDescription
npm install –productionyarn install –productionInstalls all dependencies in package.json *except* dev dependencies
npm inityarn initCreates a new package.json file / project setup
npm runyarn runRuns scripts from your package.json file
npm testyarn testRuns tests from your package.json file
npm publishyarn publishPublishes your package/td>
npm cache cleanyarn cache cleanClears the global package cache
npm loginyarn loginLogs a user into an package register

Importing Lodash into your Angular project may be slightly controversial in some circles, but I often find myself adding it (and adding the few KB that comes along with it), rather than reinventing the wheel in each project. If you’ve never used it before, Lodash is a javascript library that provides handy data manipulation extension methods for arrays/collections. If you’ve used C# LINQ, you’ll probably be familiar with many of the methods like OrderBy, GroupBy, Join etc. It’s also important to note that it pays to check if the default Array type in Javascript has what you need as there is a little bit of overlap (For example Find exists on both an array and Lodash).

But that’s not all! One of the most common use cases for Lodash is actually manipulating objects that aren’t arrays. For example, an extremely common use-case for me is using the _.clone() method which copies all the values of an object into a new object that’s safe for editing. This is extremely common for me when I’m doing two way data binding on a form that a user can “cancel”, so I still have the original object in tact.

In anycase, this post isn’t a pros and cons guide to using Lodash, it’s about adding it to your Angular project, so let’s get on and do that!

Adding Lodash To Angular

The first thing you want to do is add the Lodash package to your project using NPM/Yarn.

NPM

npm i lodash --save

Yarn

yarn add lodash

This adds Lodash to our project and at this point is ready for use, but it’s just the raw library. What we also need is the type definitions to give us some nice strongly typed defintions inside Typescript. For that we need to install one more package.

NPM

npm i --save-dev @types/lodash

Yarn

yarn add @types/lodash --dev

Note that we only add the type definitions as a dev dependency as it is not required at runtime, only while you are developing your project.

Anywhere in your project, you should now be able to import Lodash like so :

import * as _ from 'lodash';

let myItem = {};
let clonedItem = _.clone(myItem);

If you’re coming from a language such as Java or C#, the concept of constructor overloading is a pretty common one. But as a refresher in say C#, we can overload constructors like so :

class MyClass
{
	public MyClass(string value)
	{
		this.value = value;
	}
	
	public MyClass(int value)
	{
		this.value = value.toString();
	}

	private string value;
}

Whereby we create two different constructors, that take two very different parameters, to construct our object. It should also note that in C#, there is no limitation on how different these constructors can be. For example you can even have a different number of parameters :

class MyClass
{
	public MyClass(string value)
	{
		this.value = value;
	}
	
	public MyClass(string first, string second)
	{
		this.value = first + second;
	}

	private string value;
}

In Typescript/Angular, things aren’t so easy. If we tried the above code in Typescript such as :

export class MyClass
{
	constructor(value : string)
	{
		this.value = value;
	}
	
	constructor(value : number)
	{
		this.value = value.toString();
	}

	private value : string;
}

You will get the following error :

Module parse failed: Duplicate constructor in the same class 

You’ll probably also see something like :

error TS2392: Multiple constructor implementations are not allowed.

But you’ve heard that Typescript *does* support constructor overloading, so what gives? Why doesn’t this work.

Overloading Constructors In Typescript

Overloading in Typescript is a little different to what you might expect, while you can overload constructors, you cannot have multiple implementations. What this means in practice is that you either can create a union type such as :

export class MyClass
{
	constructor(value : string | number)
	{
		this.value = value.toString();
	}

	private value : string;
}

Where the constructor can accept one of either types, and you handle both within the same implementation. In this example we can handle both easily because calling toString() on a string is a safe operation, but otherwise you have to resort to using typeof checks :

export class MyClass
{
	constructor(value : string | number)
	{
		if(typeof value === "string")
		{
			this.value = value;
		}

		if(typeof value === "number")
		{
			this.value = value.toString();
		}
	}

	private value : string;
}

Honestly for me, this just looks like a mess and is hardly worth the hassle.

It get worse (just in my opinion), when you want a differing amount of parameters for two different constructors. What you need to instead do is create a single implementation that each constructor can “fall down” to the next without breaking. For example :

export class MyClass
{
    constructor(value : string)
    constructor(first : string, second? : string)
    {
        if(!second)
        {
            this.value = first;
        }else 
        {
            this.value = first + second;
        }
    }

    private value : string;
}

The constructor with a single “value” is able to fall down to the second constructor because the second parameter is nullable. We can determine which constructor was used by checking if the second value is set or not.

Again, less than ideal. I find the biggest issue is as you add constructors, removing one in the “middle” of the list suddenly breaks everything. In my opinion, this way of overloading constructors in typescript is extremely brittle and prone to giving developers headaches.

A Better Way With Static Factories

Because of the way constructor overloading works, it can be hard to wrangle if you have wildly different constructor implementations depending on the parameters. For this reason I use a simple  static factory pattern.

export class MyClass
{
    static fromSingleValue(value : string) : MyClass {
        var result = new MyClass();
        result.value = value;
        return result;
    }

    static fromTwoValues(first : string, second : string) : MyClass {
        var result = new MyClass();
        result.value = first + second;
        return result;
    }

    private value : string;
}

Now I can just call MyClass.fromSingleValue(‘something’) to get a class constructed for me. Better yet, any developer looking at this will find it extremely easy to follow, even if they’ve never used Typescript before.

Where static factories really come into their own is when you are consuming an entire other object :

export class MyOtherClass
{
    public value : string;
}

export class MyClass
{
    static fromSingleValue(value : string) : MyClass {
        var result = new MyClass();
        result.value = value;
        return result;
    }

    static fromTwoValues(first : string, second : string) : MyClass {
        var result = new MyClass();
        result.value = first + second;
        return result;
    }

    static fromMyOtherClass(myOtherClass : MyOtherClass) : MyClass {
        var result = new MyClass();
        result.value = myOtherClass.value;
        return result;
    }

    private value : string;
}

Now adding and removing constructors does not break any other constructor, and they all act independently from one another.

Detecting clicks on a particular component/element that you have complete control over is fairly trivial in Angular, but what about if you want to know when a user clicks somewhere on the page on something that is *not* inside a particular component. It may sound like a weird edgecase but it’s actually really common when building things like custom popup modals or even just custom dropdown/select controls. With these you often want to detect if a user clicks away from the component so that you can hide the modal/popup or slide up the dropdown control.

I couldn’t find anything really talking about this particular issue but as it turns out, it’s pretty easy to get up and running!

Let’s assume I have a component called “DropDownComponent” that I want to detect if there are clicks *outside* of this control. Basically if there is a click anywhere else on the webpage. The first thing we have to do, is inject a reference to ourselves in the constructor.

constructor(private elementRef: ElementRef) {
}

When ElementRef is injected this way, Angular injects in the HTML native element that this component is drawn into, which is perfect for us because now we know in plain javascript/html, what our element is referenced as.

The next piece of the puzzle is actually very trivial. Inside our component we add a HostListener that listens for any document:click.

@HostListener('document:click', ['$event.target'])
public onPageClick(targetElement) {
  const clickedInside = this.elementRef.nativeElement.contains(targetElement);
  if (!clickedInside) {
	//Do something. 
  }
}

The code itself is pretty self explanatory. We detect any document clicks (Which is a click anywhere on the page), and in doing so, we detect exactly what element was clicked. Next we determine if the element that was clicked lives inside our component by using the injected ElementRef we put in the constructor, if it doesn’t, then bingo, we know that the user has clicked outside of the component and we can run whatever custom code we wish to (e.g. Close the modal, slide up the dropdown list etc).

This probably looks a little heavy handed (And it is really… ), but a good thing to note is that HostListeners are destroyed when the component they belong to is destroyed. So unless you attach this to every single component, you’ll generally only have 1 or 2 of these document click listeners running at any one time.