If you work in Angular and haven’t heard yet, many things are in the process of switching
away from decorators over to signals. I’ve already shown you how component
or directive inputs have changed over from the @Input decorator to
signals with the input() function. In this video we’ll take a look at
how we can convert the @ViewChild and @ContentChild decorators over to signals with
the viewChild() and contentChild() functions. We’ll take an example application
that I previously created for a video demonstrating how to use the
@ViewChild and @ContentChild decorators, and we’ll switch them over to the new signal
functions producing the same end result. Ok, let’s get to it! So, here’s the demo application
that I was talking about. It lists out a searchable list of NBA players. We have created three examples. One where we used the @ViewChild decorator and
a template reference variable to select the HTML input element for our search field
in order to focus it on initialization. Then, we have another example also using the
@ViewChild decorator along with a component to focus the search form, on initialization,
but when it’s nested within another component. And, for the final example we used
the @ContentChild decorator to focus the search field when it’s been
included within projected content. And in this video, we’ll convert
each of these over to signals using the viewChild() and contentChild() functions. Ok, let’s start with the @ViewChild and
template reference variable DOM element example. Ok, here in this example we can
see, in our search component, we have the @ViewChild decorator that queries for
a template reference variable named “searchField”. And if we switch over to the template, we can
see the variable on our search input here. Ok, let’s switch back to the code. A couple more things to note, we’ve also made this decorator static
because we know it will always exist. When we do this, it becomes available earlier
in Angular’s component lifecycle events, so we’re able to set focus
in the ngOnInit() hook here. If it wasn’t static, we’d need to add
it in the ngAfterViewInit() lifecycle event instead because it wouldn’t be
available until the view is ready. Ok, now that we understand what’s going on
here, let’s switch to the viewChild() function. This function is part of a set of what
are known as “signal queries” in Angular. These queries are used to find child
components, directives, or DOM elements. If we’re searching for something directly within
the component template, then we’re using the view. To contrast, if we’re searching for something that will be inserted within a
content slot, we’re using content. In this case, since we’re
dealing with the component view, this function is considered a “view query”. Anything dealing with projected content
would be considered a “content query”. We’ll see an example of
this later on in the video. Ok, to use the viewChild() function, we’ll create
a private variable that we’ll name “searchField” . Since we’re going to replace our existing “searchField” property it’ll
be named the same for now. We’ll remove the old one once we’re done. Then, we just add the viewChild() function. And we’ll need to type it to
an ElementRef, HTMLElement. Ok, now we can remove the old
variable set with the decorator. And, we can remove the decorator import too. Then, all we need to do is, since
this property is now a signal, we need to add parenthesis where we’re using it. And that’s it. If we save, we should see that our search field is focused on initialization just
like it was before the change. And there we go, so that works properly. Now, since this is now a signal, we can switch this over to use an effect()
instead of the ngOnInit() lifecycle event. This will make it available as soon
as the viewChild() signal is set. To do this, let’s add a constructor(). Within the constructor, let’s
add the effect() function. And within the callback here,
we can move our focus call. Now we can remove our ngOnInit()
function, interface, and import too. Now, we should be able to save and
see this still working correctly. And there we go. Pretty cool. One more thing we can do here is, since
we know that this field will always exist, we can make it required just like
we can with the input() function. We just add, dot, required. Now, Typescript will infer that
this variable will always exist, meaning we no longer need the question mark here. Ok, that’s it. If we save again, everything
should work as expected. And it does, cool. Now, this would mean that we’d
get an error if this were to become conditional or something in the future. So that’s the viewChild() function querying for an HTML element , now let’s query for a
component within the template instead. Here, in this example, we have moved
the search form to its own component. Then, this search form component gets
included within the parent search component. When we switch back over to the
code for the search form component, we can see that we have our “searchField”
variable available as a public property so that it can be accessed externally
by other components or directives. Now, let’s take a look at the
code for the search component. We have an old @ViewChild decorator used to
access the SearchComponent within the view. It’s also static like the previous
example and it’s accessed in the ngOnInit() lifecycle hook to set focus
of the text field within that component. This example will be very similar to the last one accept we’ll be using a component for
our query instead of an HTML element. So, let’s add a new private
variable named “searchForm”. Then we’ll use the viewChild() function again. We’ll make it required. Then, we’ll pass it the SearchFormComponent class. And that’s it. Now we can remove the old
property, set with the decorator. We can remove the import for
the @ViewChild decorator. Next, we need to add parenthesis
to the “searchForm” property. Next, we can add a constructor(). Then we can add the effect() function. Now we can move the focus call
into the effect function callback. And last but not least, we can
remove the ngOnInit() function, the interface, and the import too. And that should be it. Let’s save, and make sure
it’s still working properly. There it is, nice. So now we know how to query for both DOM elements
and components or directives within the view. Up next, let’s look at how we’d
use the contentChild() function to query for a component within projected content. Ok, in this example, we have our same search form component, but the parent search
component has changed a little. If we look at the template, we can
see that it has a content slot. So, let’s take a look at the app component
template where this search component is used. Here, we can see that the search form component is
included in the slot for the search component now. So it’s projected content
within the search component. So now to access this search form component from the parent search component we’ll
be dealing with a content query. Let’s look at the code for the search component. Here we can see that we’re using
the old @ContentChild decorator to create a static “searchForm” property
querying for the SearchFormComponent. So, this is exactly the same as
the last example accept we’ll use the contentChild() function in this case. So, let’s create a private
field named “searchForm”. Then we’ll use the contentChild() function. We can make it required here too
since we’ll always want to include it. And then we just pass the
SearchFormComponent class. Now we can remove the old property
set with the @ContentChild decorator. We can remove the decorator import too. Now we can add a constructor. Then we can add the effect function. Then we can move the focus call into the effect. We then need to add parenthesis to
the “searchField” property here. Ok, all that’s left now is to remove the
ngOnInit() function, interface, and import. Cool. Now let’s go ahead and save to make
sure everything is working correctly. Nice, it’s focused just like we want. So, there you have it. Now you know how use the new viewChild()
and contentChild() signal query functions. They’re not too much different than the old
decorators but you’ll probably want to start using them moving forward to modernize your
Angular applications and now you’ll know how. That’s all for now. Thanks for watching.