Inter-Component Communication In Angular – Input Binding

This article is part of a series on Inter-Component Communication in Angular. While you can start anywhere, it’s always best to start at the beginning right!

Part 1 – Input Binding
Part 2 – Output Binding
Part 2 – Joining Service (Coming Soon!)
Part 3 – ViewChild (Coming Soon!)


Inter-Component communication refers to the way that we can get components talking to each other. Sounds simple?! And for simple scenarios where there is a direct child to parent relationship, it is! But when components are not directly placed within one another, or they need to do more than just pass a variable back and forth, things can actually get complicated fast.

Rather than start at the complicated scenarios, we are still going to cover off the basics such as Input binding as for the most part, this is going to be your bread and butter. Then later on, we can start getting into more complicated scenarios such as using a joining service, or using the ViewChild declaration!

The first way we are going to get our components talking to each other is using one-way binding with the “Input” keyword. This is going to be by far the most common method of passing values into a child component, and it’s also one of the easiest. So without further ado, let’s get going!

Basic Input Usage

At it’s simplest, a component can use the “Input” declaration on a component, to say that any parent can pass in a value. For example, our ChildComponent may look like so :

export class ChildComponent {
  @Input() message : string;
}

And when we place this component on any page, we can do something like so :

<app-child [message]="'Hello World!'"></app-child>

Easy right! There really isn’t much to it!

Why The Double Quotes?

A common question I get asked is why do we double quote the text going in? e.x. Why do we have both ” and ‘ wrapped around our Hello World?

[message]="'Hello World!'"

The answer is actually quite simple. When we use the square brackets syntax [] it means we are doing a “one way” bind (From our parent to our child). This one way binding syntax expects either a variable, or a constant. However I often see people making the mistake of doing this :

[message]="Hello"

Since the message input variable is a string, what’s the big deal right? Well when you don’t wrap ‘Hello’ in single quotes, what you are telling Angular is that you are passing a variable. In this case, Angular looks for a variabled named “Hello”, doesn’t find one, then passes through an undefined value. This is why you often single variables wrapped in single quotes, even when passing strings through.

Timing Is Everything

The next problem people generally run into with Input variables is to do with timing. Often, start up code will live in an ngOnInit like so :

export class ChildComponent implements OnInit {
  @Input() message : string;
  
  constructor() { }

  ngOnInit() {
    console.log(this.message);
  }

}

But what happens if message is not set by the time the component is initialized? A common reason for this is that maybe string comes from an API, and therefore there is a bit of a race condition with the API returning and passing the message into the child component, and the child component actually being initialized.

For these sorts of race conditions, I generally go with property getters and setters. That means turning my component into something more like so :

export class ChildComponent implements OnInit {

  _message : string;
  @Input() set message (value : string) {
    this._message = value;
    this.setup();
  }
  
  constructor() { }

  ngOnInit() {
  }

  setup() {
    console.log(this._message);
  }

}

Now it doesn’t matter on the timing of when a message will be set. Each time the input variable is changed, we will kick off the setup routine. I use this in almost all of my projects as it’s a pretty quick and simple way to allow async one way bindings to occur, where I don’t need to manually be checking change tracking myself.

Timing Complex Objects

Our above example works because when a new message is passed in, the reference changes and the setter is called. But imagine we have a more complex object like so :

export class ChildComponentConfiguration {
  message : string;
}

With our child component like so :

export class ChildComponent implements OnInit {

  _config : ChildComponentConfiguration;
  @Input() set config (value : ChildComponentConfiguration) {
    this._config = value;
    this.setup();
  }
  
  constructor() { }

  ngOnInit() {
  }

  setup() {
    console.log(this._config.message);
  }

}

Imagine that we pass in our configuration object, then at a later point, the message object is changed. Under this scenario, our setter will never be called because the configuration object itself has not changed, only the properties within it.

Now, what I’m going to suggest has it’s own pros and cons. You should definitely read more about implementing your own DoCheck methods, but.. it works a bit like this :

export class ChildComponent implements OnInit, DoCheck {
  @Input() config : ChildComponentConfiguration;
  
  differ: any;
  
  constructor(private differs: KeyValueDiffers) { 
    this.differ = differs.find({}).create();
  }

  ngDoCheck(): void {
    const changes = this.differ.diff(this.config);
    if(changes) {
      changes.forEachAddedItem(change => {
        //The message has changed. 
        if(change['key'] == 'message')
        {
          this.setup();
        }
      });
    }
  }

  ngOnInit() {
  }

  setup() {
    console.log(this.config.message);
  }
}

Let’s walk through this a little. The first thing you will notice is that we implement the “DoCheck” interface, this means that when ChangeTracking is called on our component, we can implement custom logic to say what’s changed.

In our constructor, we take what’s called a “KeyValueDiffers”. The main thing to note is that this allows us to compare “differences” between an object each time the DoCheck method is called. Essentially, if the message has changed between two calls of ngDoCheck, then the differ will pick it up.

Finally, once we have the changes we need to loop through them and check them. We do this by checking the change “key” which is the name of the property that changed. If it’s the “message” that’s changed, then we call setup.

There’s obviously come caveats with using this :

  • ngDoCheck is called very often. It shouldn’t be taken lightly as it can cause an app to slow to a crawl. Much like using an impure pipe!
  • The differ can actually know when values are unset or set for the first time instead of modified, so play around with it a little bit
  • Again, you only need this if a property of your input variable is “delayed” being changed, but that could even be because a user must click a button first, not just waiting on API calls.

Overall, implement DoCheck is a heavy handed approach, but it’s one to keep in your tool arsenal if you have no other option.

Next

Coming up next is using EventEmitters to handle events being passed up from child to parent! Check it our here : https://tutorialsforangular.com/2021/04/04/inter-component-communication-in-angular-output-binding/

Leave a Reply

Your email address will not be published. Required fields are marked *