v19 launch sale ends in ... Get 25% off BUY NOW
3d cartoon hands holding a phone

Unlock full course by purchasing a membership

Lesson 7

Overview of Signal APIs in Angular

STANDARD

An Overview of Signal APIs in Angular

We’ve already looked at the basic primitive building blocks of signals in Angular which are: signal, computed, and effect.

However, we are not just given these bare bones primitives to make use of ourselves. Angular is also further integrating these into Angular itself and exposing core functionality via signal APIs.

input

We have already seen one example of this: input. In the case of inputs, we don’t need to actually create any signals ourselves. We just use the input function provided by Angular to define an input, and then we get that input given to us as a signal within the component.

This is particularly useful in the case of inputs, because we could then use other signal primitives in combination with that.

For example, if we want to derive some other value from an input:

name = input.required<string>();
reversedName = computed(() => [...this.name()].reverse().join(""))

NOTE: I am making use of required here which is a way to force an input to be supplied to the component — if we don’t supply a name input to this component it would cause an error.

Now we take the value of name and create a new computed signal that reverses the name. The cool thing is that whenever name changes our reversedName will update automatically.

What if we wanted to run some side effect when name changed? We can easily do that too:

name = input.required<string>();

constructor(){
  effect(() => {
    console.log(this.name())
  })
}

Now every time the name input changes, our effect function will run.

View Queries

We will be diving more into the specifics of view queries as we use them in the applications we will build, but this is another story very similar to the migration from @Input to input.

In some cases, we might want to grab a reference to some element in our template.

In a previous lesson, we saw that we could create a template variable like this:

<p #myParagraph></p>

If we wanted to grab a reference to that in our class we could use a “view child” to do that. Previously, with the old decorator based approach, that would have looked like this:

@ViewChild('myParagraph') myParagraph;

With the new signal based API it looks like this:

myParagraph = viewChild('myParagraph');

Again, it is the same story as input where myParagraph is now provided as a signal which means we would access its value like this: this.myParagraph().

Also, just as with inputs, we can do things like create derived values from this signal using computed or run side effects with effect.

As well as viewChild we also have viewChildren for retrieving multiple references, and then we also have contentChild and contentChildren for situations where we are dealing with “projected” content. We are going to make use of this a little later, but the general idea with “projected” content is that rather than having some content directly in the template of a component like this:

<div>
  <p>I am directly in the template, I'm a view child!</p>
</div>

We might want to supply content dynamically to a component — this is sort of like supplying an input but it’s part of the template. To do this, our child component would use ng-content like this:

<div>
  <ng-content></ng-content>
</div>

Now there is no p tag directly in the template; it is not a “view child”. But now, in the parent component, we can dynamically pass in this part of the template:

<app-my-comp-with-content-projection>
  <p>I am passed in from the parent, I will be a content child!</p>
</app-my-comp-with-content-projection>

The end result in the DOM will be the same, e.g:

<div>
  <p>I am passed in from the parent, I will be a content child!</p>
</div>

Everything from within the opening/closing tags of the component in the parent component gets “projected” into the child component wherever the ng-content tag is placed.

model

A model looks deceptively like an input. One basic use case it enables is that it allows a component that defines an “input” to also modify the value of that input.

For example, in this case:

import { Component, input } from '@angular/core';

@Component({
  selector: 'app-welcome',
  template: ` <p>Hi, {{ name() }}!</p> `,
})
export class WelcomeComponent {
  name = input('friend');
}

The name input can only be changed by passing the value in through the parent component:

<app-welcome [name]="user.name" />

If we wanted to set the value of name from within the WelcomeComponent we would not be allowed to do that as an input signal is read-only:

import { Component, input } from '@angular/core';

@Component({
  selector: 'app-welcome',
  template: ` <p>Hi, {{ name() }}!</p> `,
})
export class WelcomeComponent {
  name = input('friend');

  resetName(){
    // does NOT work
    this.name.set('');
  }
}

However, model will allow us to do that:

STANDARD
Key

Thanks for checking out the preview of this lesson!

You do not have the appropriate membership to view the full lesson. If you would like full access to this module you can view membership options (or log in if you are already have an appropriate membership).