Skip to main content

Digital Transformation

Proper Communication between Angular Components

Introduction

A few months ago I had a solution that required me to call a method in a child component. I, not knowing how to do this properly, “hacked” a solution using Angular inputs and outputs. Although the solution technically worked, it never felt like it was the best solution to use. After communicating with some coworkers, I found out that my solution wasn’t following “best practices” for communicating between Angular components. The best way to use a child component’s methods and properties is to use the ViewChild or ViewChildren decorator.

Before I explain the differences between Angular inputs/outputs and the ViewChild/ViewChildren decorator use cases, I want to first define the relationship between a parent component and a child component:

@Component({ 
   selector: 'parent', 
   template: ' 
      <div>
         <child></child>
      </div> 
   '
}) 
export class ParentComponent { 

}

@Component({
   selector: 'child',
   templateUrl: './child.component.html'
})
export class ChildComponent {

}

A parent/child relationship is defined by one component (child) being used within another component’s (parent) template. As you can see above, the child component’s selector is being used within the parent component’s template.

Inputs/Outputs

@Component({
   selector: 'parent',
   template: '
      <div>
         <child [dataArray]="parentArray"></child>
      </div>
   '
})
export class ParentComponent {
   parentArray: number[];
}

@Component({
   selector: 'child',
   templateUrl: './child.component.html'
})
export class ChildComponent {
   @Input() dataArray;
}

Angular inputs/outputs should be considered when the desire is to share data between child and parent. For example, if there is data in the parent that the child component needs to display, you should create an input on the child component so the parent can pass data through. As seen above, the parent is passing through the variable “parentArray” into the child, making it usable by the child component.

In the case that there is data in the child that the parent needs to use, then an Angular output can be used.

@Component({
   selector: 'parent',
   template: '
      <div>
         <child (childArray)="saveArray($event)"></child>
      </div>
   '
})
export class ParentComponent {
   childArray: number[];

   saveArray(array: number[]) {
      this.childArray = array;
   }
}


@Component({
   selector: 'child',
   templateUrl: './child.component.html'
})
export class ChildComponent {
   @Output() event: EventEmitter<any> = new EventEmitter();

   sendArray(array: number[]) {
      this.event.emit(array);
   }
}

When an Angular output is combined with an Event Emitter, the child component can send an array to the parent. This array is passed into the parent function saveArray() where it is then saved to its own variable. Angular inputs/outputs are great for sending data, but they are not intended to be used when trying to access a child’s properties and methods – that is what the ViewChild/ViewChildren property decorator is for.

ViewChild/ViewChildren

@Component({
  selector: 'parent',
  template: `
    <div>
      <child #childSelector></child>
    </div>
  `
})
export class ParentComponent implements AfterViewInit {
   @ViewChild('childSelector', { static: false }) childComponent: ChildComponent

   ngAfterViewInit() {
      childComponent.foo();
   }
}

@Component({
   selector: 'child',
   templateUrl: './child.component.html'
})
export class ChildComponent {
   foo() {
      // Do Something
   }
}

ViewChild is a property decorator that selects a child component within the parent component’s template. As seen in the example above, the parent component uses the @ViewChild property decorator in order to select the child component (using the selector ‘#childSelector’) from it’s template. Once the child component is selected, it can call any of the child’s public methods. In this case, the parent component is calling the foo() method directly. Its important to point out that the parameter { static: false } used inside the decorator is a required parameter as of Angular 9. This parameter, if true, allows the child component to be selected during NgOnInit().

ViewChildren is a property decorator that selects multiple children components within the parent component:

@Component({ 
   selector: 'parent', 
   template: `
      <div>
         <child #childSelector id="1"></child>
      </div>
      <div>
         <child #childSelector id="2"></child>
      </div>
   ` 
}) 
export class ParentComponent implements AfterViewInit { 
   @ViewChildren('childSelector') childComponents: QueryList<ChildComponent> 
   
   ngAfterViewInit() { 
      childComponents.foreach(child => {
         child.foo();
      }); 
   } 
} 

@Component({ 
   selector: 'child', 
   templateUrl: './child.component.html' 
}) 
export class ChildComponent { foo() { // Do Something } }

In the above example, I am able to select multiple child components as a QueryList<>. This list is then iterated through to call the child function foo() for each child component from the parent component.

Conclusion

Because there are multiple ways Angular components can communicate, it is important to understand the purpose of the communication. While Angular inputs/outputs should be used when sharing data to and from child components, ViewChild should be used when trying to utilize properties and methods of the child component directly in the parent component.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Hunter Smith, Technical Consultant

Hunter is a Technical Consultant with the Microsoft Business Unit at Perficient. His work at Perficient has primarily been as a Full-Stack, Custom Dev Web App Developer. As such, he works with clients to provide custom technical solutions using Angular, React, C#, SQL, etc.

More from this Author

Follow Us
TwitterLinkedinFacebookYoutubeInstagram