Skip to content

Modus-Logo-Long-BlackCreated with Sketch.

  • Services
  • Work
  • Blog
  • Resources

    OUR RESOURCES

    Innovation Podcast

    Explore transformative innovation with industry leaders.

    Guides & Playbooks

    Implement leading digital innovation with our strategic guides.

    Practical guide to building an effective AI strategy
  • Who we are

    Our story

    Learn about our values, vision, and commitment to client success.

    Open Source

    Discover how we contribute to and benefit from the global open source ecosystem.

    Careers

    Join our dynamic team and shape the future of digital transformation.

    How we built our unique culture
  • Let's talk
  • EN
  • FR

Accessing Child Elements in Angular / Ionic

Published on September 29, 2017
Last Updated on April 8, 2021
Application Development

Components are at the heart of Angular, and we have some very useful tools to work with them efficiently. Often, we need to access the children – child elements, in other words – of our components. This can mean getting a reference to the DOM element, or to the actual component object if one exists. For this, we have at our disposal two handy decorators @ViewChild and @ContentChild, along with their list counterparts – @ViewChildren and @ContentChildren. Let’s see why there are two of these and how we can use them.


NEW RESEARCH: LEARN HOW DECISION-MAKERS ARE PRIORITIZING DIGITAL INITIATIVES IN 2024.

Get Report


Before we begin

We’ll create a very simple counter component. All it will do is display its property and have a method we can use to increment it. We’ll demonstrate how to access this component’s increment method to increase the value later.

@Component({
  selector: 'counter',
  template: '<h2>{{counter}}</h2>'
})
export class Counter {
  counter: number = 123;
  
  increment() {
    this.counter++;
  }
}

Children in component’s template

There are two places a component can have child elements – template and content. Let’s talk template children first. Observe the following component:

@Component({
  selector: 'my-app',
  template: `
  <my-dialog>
    <counter></counter>
  </my-dialog>
  `
})
export class AppComponent { }

Here, both – my-dialog and counter are children of AppComponent in its template. Let’s assume that we have my-dialog defined for now. If we want to access the counter here, we’ll use the @ViewChild decorator as follows:

@Component({
  selector: 'my-app',
  template: `
  <button (click)="counter.increment()">Increment View Child </button>
  <my-dialog>
    <counter></counter>
  </my-dialog>
  `
})
export class AppComponent {
  @ViewChild(Counter) counter: Counter;
}

We’ve added a button to make it easy to play with this example. The @ViewChild decorator can take either a type of component or a selector (string) as argument, and returns the first child element from template that matches it. In this case, since we have a Counter component, we get the reference to actual component object that’s rendered. Clicking the button increases the counter to 124, proving this.

Children in component’s projected content

In the last example, since both – my-dialog and counter are present inside the template of AppComponent, we can access them with @ViewChildren. However, counter also seems to be a child of my-dialog, although not in its own template. Here’s the definition of MyDialog as we have it for now:

@Component({
  selector: 'my-dialog',
  template: `<ng-content></ng-content>`
})
export class MyDialog { }

All this component does is project the content that was provided as its child. This is referred to as transclusion. That said, there is a way of accessing the children in the projected component – @ContentChild.

Let’s modify our MyDialog component a bit. Here’s what the entire code ends up looking like:

@Component({
  selector: 'my-dialog',
  template: `
  <button (click)="counter.increment()">Increment Content Child </button>
  <ng-content></ng-content>
  `
})
export class MyDialog {
  @ContentChild(Counter) counter: Counter;
}

@Component({
  selector: 'my-app',
  template: `
  <button (click)="counter.increment()">Increment View Child </button>
  <my-dialog>
    <counter></counter>
  </my-dialog>
  `
})
export class AppComponent {
  @ViewChild(Counter) counter: Counter;
}

Both – AppComponent and MyDialog are accessing the same Counter component. AppComponent is using @ViewChild because <counter> is present inside its template. On the other hand, MyDialog is using @ContentChild because <counter> is a part of its projected content, not the template itself.

Matching more than one child

Both the decorators we saw above have a corresponding list version, which matches all the child elements in its scope (template or projected content). Unsurprisingly, they are named @ViewChildren and @ContentChildren. Now that we know the difference in where they look for children, let’s see how we can use @ViewChildren quickly.

@Component({
  selector: 'my-app',
  template: `
  <button (click)="counter.increment()">Increment View Child </button>
  <my-dialog>
    <counter></counter>
  </my-dialog>
  
  <counter></counter>
  <counter></counter>
  <counter></counter>
  <counter></counter>
  <button (click)="incrementChildren()">Increment View Children </button>
  
  `
})
export class AppComponent {
  @ViewChild(Counter) counter: Counter;
  @ViewChildren(Counter) counters: QueryList<Counter>;
  
  incrementChildren() {
    this.counters.forEach(counter => {
      counter.increment();
    });
  }
}

We’ve added a bunch of <counter> elements and a button to increment all of them to our template. To support that, we’ve added @ViewChildren decorator to capture all the counter children. It’s important to note that @ViewChildren and @ContentChildren do not return an array. They return a QueryList, which is an array like object. It is an iterable, which means we can use that with ngFor, and has common array methods like forEach which we’ve made use of. The QueryList automatically tracks addition and removal of any children.

Here’s the finished live plunk of this.

In the plunk, notice that clicking on the button to increment view children increments all the counters, including the one we’ve matched with @ViewChild and @ContentChild. Clicking on the corresponding button to increment content and view child continues to increment only the one counter that is matched.

When a child can be of more than one type

Consider the following bit of code:

@Directive({
  selector: '[highlight]'
})
export class Highlight {
  constructor(private el: ElementRef) { }
  
  changeColor() {
    this.el.nativeElement.style.color = 'red';
  }
}

@Component({
  selector: 'my-app',
  template: `
  <counter highlight></counter>
  <button (click)="counter.increment()">Increment View Child</button>
  `
})
export class AppComponent {
  @ViewChild(Counter) counter: Counter;
}

What if we wanted to match the <counter> child, but instead of getting the component object back, needed a reference to the attached directive object? In other words, how can we access the changeColor method on our view child?

Fortunately, there’s an easy way to do so. But before we get into that, we need to understand how angular decides what to return to us when it sees a @ViewChild decorator. Up till now, we were getting the component object, which allowed us to interact with component’s methods directly. However, that’s not the end of it. For angular, there can be multiple ways to interpret view-child. It is first and foremost a DOM element. It may also correspond to an angular component. Further, it may also have directives or services attached to it. We can help by telling explicitly what we want.

Turns out, in addition to providing the type/selector of child to match against, we can also provide instructions of interpreting the child object via read property. Our decorator will change thus:

@ViewChild(Counter, {read: Highlight}) counter: Highlight;

Now, counter.increment() will result in error, because counter is no longer a component object. Instead, it represents the directive object, which means counter.changeColor() is the new black.

@Component({
  selector: 'my-app',
  template: `
  <counter highlight></counter>
  <button (click)="counter.increment()">Increment View Child</button>
  <button (click)="highlight.changeColor()">Color View Child</button>
  `
})
export class AppComponent {
  @ViewChild(Counter) counter: Counter;
  @ViewChild(Counter, {read: Highlight}) highlight: Highlight;
}

Note that although both the @ViewChild decorators return references to different types of things, in the end, it is all affecting the same element. By specifying the type of reference we want, we can easily switch between our interpretations of an element.

The finished plunk contains another interpretation we can use – as a DOM element – and accesses the native element directly.

Summing up

We saw how we can access child elements using different decorators that angular provides, depending on where those elements might occur. We also learned how we can specify the type of child element directly, and interpret same element in different ways. This opens up new ways of interaction between components in complex scenarios when communication through input/output might not be the best (or practical) approach.

Need efficient and scalable software solutions? Learn more about our software development expertise.

Posted in Application Development
Share this

Akash Agrawal

Akash Agrawal is a Software Engineer with Modus Create. He is an experienced JavaScript and Ruby programmer with chops in front and backend development along with database architecture. Akash has a deep interest in pure functional languages and is a padwan haskell hacker. He enjoys exploring surrounding areas on his bike when not working.
Follow

Related Posts

  • Angular Elements - Your ngComponents Everywhere
    Angular Elements -- Your ngComponents Everywhere

    If you’re reading this, you already know Angular. So whether you’ve worked on AngularJS (1.x)…

  • Ionic 2 Project Structure
    Ionic 2 Project Structure

    This is the first part in a series of introductory posts on Ionic 2. If…

Want more insights to fuel your innovation efforts?

Sign up to receive our monthly newsletter and exclusive content about digital transformation and product development.

What we do

Our services
AI and data
Product development
Design and UX
IT modernization
Platform and MLOps
Developer experience
Security

Our partners
Atlassian
AWS
GitHub
Other partners

Who we are

Our story
Careers
Open source

Our work

Our case studies

Our resources

Blog
Innovation podcast
Guides & playbooks

Connect with us

Get monthly insights on AI adoption

© 2025 Modus Create, LLC

Privacy PolicySitemap
Scroll To Top
  • Services
  • Work
  • Blog
  • Resources
    • Innovation Podcast
    • Guides & Playbooks
  • Who we are
    • Our story
    • Careers
  • Let’s talk
  • EN
  • FR