DEV Community

Explaining Angular concepts to grandma 👵 - ViewRef / View ContainerRef

Prologue: If you're looking for a deep technical breakdown, this isn't the place. My goal is to explain things in a way that even my grandmother could grasp—at least a little. This post reflects my personal understanding and mental model of the topic, intentionally keeping things non-technical as much as I can. That said, I strive for accuracy, so if I’ve got something wrong, I encourage you to correct me in the comments, and I’ll update the post. Learning is, after all, a collaborative process 🚀

One of the things that required a bit of digging for me to grasp is the concept of ViewRef and ViewContainerRef.

Sure, it sounds straightforward on the surface: a ViewRef is obviously a reference to a view, a ViewContainerRef is a reference to a view container.

But what really is a view?

According to the Angular documentation, a view is:

The smallest unit of the UI that represents a portion of the screen rendered.

So, in my mind, I ask myself:

"Okay, so... if I have a simple <p></p> element, is that a view?"

I mean, it checks all the boxes:

  • It's a "unit of the UI" ✅
  • That "represents a portion of the screen rendered" ✅

However, this is not the case. A <p></p> element is not a view. Why?

What makes a view a "view"? 🤔

Think about the example above: if you had a component with a hundred <p></p> elements, and each one of those <p></p> elements created a view, you would have 100 views. That sure sounds like a lot to manage, even for a computer.

As such, in my opinion, saying a view is "The smallest unit of the UI that represents a portion of the screen rendered" is misleading.

I think a better way of describing a view is:

Any bit of your template that can change

What do you mean by "change"? 🔁

Consider this simple component that represents a list of fruits:

@Component({
  selector: 'app-fruit-list',
  templateUrl: './fruit-list.component.html'
})
export class FruitListComponent {}
Enter fullscreen mode Exit fullscreen mode
<!-- fruit-list.component.html -->
<ul>
    <li>Apples</li>
    <li>Bananas</li>
    <li>Pears</li>
</ul>
Enter fullscreen mode Exit fullscreen mode

The HTML markup above is static. There's no Angular stuff there that can either add or delete a list item or hide the list altogether. You are stuck eating apples, bananas and pears.

Now imagine we change our component to this:

@Component({
  selector: 'app-fruit-list',
  templateUrl: './fruit-list.component.html'
})
export class FruitListComponent {
  public fruits = []

  protected fruitsService = inject(FruitsService)


  ngOnInit(){
    this.fruits = this.fruitsService.fetchFruits()
  }
}
Enter fullscreen mode Exit fullscreen mode
<ul *ngFor="let fruit of fruits">
    <li>{{fruit}}</li>
</ul>
Enter fullscreen mode Exit fullscreen mode

Now our list of fruits can have any number of items. Maybe our fruit supplier only has 10 different fruits available. Or maybe it has a million different fruits. Maybe in the morning apples are available but sold out in the afternoon. We don't know. And as it turns out Angular doesn't know either.

Because Angular doesn't know how many fruits will exist, it needs to add or remove <li> elements dynamically.

Therefore, Angular will now consider our list of fruits a 'view'—or, in 'grandma terms,' something that can change at any time.

Note: The example above uses ngFor. However other things like <ng-template> or ngIf have the same power to create "views". If it makes the UI change dynamically, it creates a "view".

A "view" inside the box 📦

You'll notice that in the previous paragraph I always referred to the bit of HTML that can change dynamically as a "view". Notice the quotes here - "view" - as they are absolutely critical.

When you tell Angular something will have to be updated dynamically, Angular under the hood wraps that HTML element in a ViewContainerRef.

The way I think about ViewContainerRef is "a box where things are can be added, removed or moved around". What "things", you ask ? More on that in a second.

For now let's focus on counting how many "boxes" /ViewContainerRefs we have and where they come from.

Optional side note: open your editor and navigate to the ViewContainerRef class definition. Have a look at the methods there. You will see stuff like CreateComponent, CreateEmbeddedView, get, move or remove. That looks a lot like adding or removing things from a box, if you ask me.

Stacking up the boxes 📦📦

Every time Angular creates a component, you get at least one box / ViewContainerRef. This will be the ViewContainerRef for the whole of your component. Let's go back to our list of fruits component and draw some boxes and arrows:

The top level ViewContainerRef is a
The top level ViewContainerRef is a "box" that holds all of the Fruit List component HTML

Notice I said "at least" one ViewContainerRef. Why? Because if you add things to your template that change dynamically, then you can have many ViewContainerRefs. In fact, in this example we have 2 ViewContainerRefs: the top level one and the one generated by the ngIf in the ul element. If we draw more boxes and arrows, it looks like this:

Another ViewContainerRef that was there all along
Another ViewContainerRef that was there all along

Putting things in the box 📦 ⬅

Now that we have a clue of what a ViewContainerRef is and roughly what it does, let's talk about ViewRef.

According to the Angular documentation a ViewRef:

Represents an Angular view

That sounds reasonable and straightforward enough. Upon reading this, I immediately jumped to the following (wrong) conclusion:

The ViewRef is the bit of HTML inside of the ViewContainerRef. So there must be a way for me to retrieve it.

With this in mind, I tried a few things—ranging from utterly stupid to moderately stupid—but alas, to no avail.

// this is stupid, ViewRef is not even injectable 🙃
public viewReference = inject(ViewRef)
Enter fullscreen mode Exit fullscreen mode
public viewReference = this.viewContainerRef["looking for something here that returns the viewRef but such thing doesn't exist"]
Enter fullscreen mode Exit fullscreen mode

So where is this ViewRef that "represents an Angular view"? I can't seem to find it anywhere!

As it turns out, there are three very important words that, I think, were left out of that definition, and they make a big difference:

Represents an Angular view you create yourself

A ViewRef is not something that exists in the box /ViewContainerRef, it is what you get when you put something in the box. It's a reference pointing to the view you just created.

The ViewRef at the bottom of the box 🎁

As we have discussed before a ViewContainerRef is capable, among other things, of creating components dynamically (or in our grandma terms "putting things in the box").

There are a couple of methods in a ViewContainerRef that allow us to do that:

// class stuff
      protected myViewContainerRef = inject(ViewContainerRef)
//more class stuff

    someMethod(){
        const aReferenceToAComponent = this.myViewContainerRef().createComponent(SimpleComponent) //is of type ComponentRef<SimpleComponent>
        const aReferenceToAnEmbeddedView = this.myViewContainerRef().createEmbeddedView(this.someTemplate()) //is of type EmbeddedViewRef<any>
    }
Enter fullscreen mode Exit fullscreen mode

You will notice that none of those 2 methods returns a ViewRef. How come? We should have a ViewRef right? If I just put something in the box I want a reference to it.

Take a deep breath; don't sweat it. It is there—just a bit hidden.

ComponentRef has a hostView property. And that hostView property is, in fact, a ViewRef. So there's one ✅

EmbeddedViewRef extends from ViewRef, so it is, in fact, a ViewRef. So there's two ✅

We have found our ViewRefs 🎉

You will notice that you can do a lot less with ViewRef when compared with ViewContainerRef. With ViewRef you can do stuff that relates to a view (like marking it for change detection or destroy it), whereas with ViewContainerRef you can do stuff that relates to the place where the view lives (like adding new components or moving existing components around).

Wrapping it up

Hopefully all of these non-sense with boxes and fruits helps you understand the difference between ViewRef and ViewContainerRef. If you just remember one thing from this post remember the following:

One is the box; the other is what you get when you put something in the box

Happy coding!

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

While many AI coding tools operate as simple command-response systems, Qodo Gen 1.0 represents the next generation: autonomous, multi-step problem-solving agents that work alongside you.

Read full post →

Top comments (1)

Collapse
 
bhalperin profile image
Benny Halperin

Nice. Underlines Angular's complexity and challenging learning curve. I argue that even the most resilient grandma would find it impossible to grasp, and switch back to reading some bun recipe...

Postgres on Neon - Get the Free Plan

No credit card required. The database you love, on a serverless platform designed to help you build faster.

Get Postgres on Neon