I'm sure you already heard about the Angular @Input decorator!

This decorator allows us to pass data from a parent component to a child component, and it's one of the core building blocks of the framework.

What you might not know is that this decorator has a ton of extra features and options that were added over the years that you might not have heard about.

Specifically, we will focus a lot on the input transforms feature, which for most use cases can be used instead of input getters and setters or the @Attribute decorator.

Input transforms can also help us keep our templates more readable, by simplifying certain common use cases like boolean or numeric read-only properties.

So in this guide, I will explain the @Input decorator in detail with all its extra features, and provide examples for each scenario.

Table of Contents

  • The basics of how to use @Input
  • Required inputs
  • @Input with setters and getters
  • @Input with aliases
  • Input transforms
  • Built-in input transforms booleanAttribute and numberAttribute
  • @Input vs @Attribute: what are the differences?
  • When to use @Input vs @Attribute?
  • Summary

The basics of how to use @Input

@Input() is an Angular decorator that marks a class property as an input property of the component.

The @Input decorator is used to pass data from a parent component to a child component.

Here is the basic usage of the @Input decorator:

@Component({
  selector: "child",
  template: ` <p>{{ name }}</p> `,
})
export class ChildComponent {
  @Input() name: string;
}

Here the @Input decorator was used to mark the name property as an input property.

This means that a parent component using the ChildComponent can set the value of the name property using the property template binding syntax:

@Component({
  selector: "parent",
  template: ` <child [name]="name" /> `,
})
export class ParentComponent {
  name = "John Doe";
}

If the value of the name property changes in the parent component, then that change is going to get propagated via the @Input() decorator.

And that is the whole point of @Input!

Now, let's go beyon the basics. Let's start exploring what other extra features and configuration options we have available.

Required inputs

By default, inputs are optional, so there won't be a validation error when an input is not supplied.

Optionally we can mark an input as required like this:

@Component({
  selector: "app-child",
  template: ` <p>{{ name }}</p> `,
})
export class ChildComponent {
  @Input({
    required: true,
  })
  name: string;
}

In the above code, the name property is required, meaning that the parent component must supply a value for the name property.

If a value is not provided to the input property, then an error will be thrown:

[ERROR] NG8008: Required input 'name' from component ChildComponent must be specified.

@Input with setters and getters

Now let's start diving into some of the lesser-known features of @Input, starting with getters and setters.

Besides applying the @Input decorator to member variables, we can also apply it to a getter:

@Component({
  selector: "app-child",
  template: ` <p>{{ name }}</p> `,
})
export class ChildComponent {
  private myCourses: Course[];

  @Input()
  get courses() {
    return this.myCourses;
  }

  set courses(courses: Course[]) {
    // you can add here some logic to modify the courses input variable, and create a derived value
    this.myCourses = courses;
  }
}

This can be useful in situations where you want to apply some sort of transformation to the value passed to the component.

In an upcoming section, we are going to show a better way of transforming input values.

But let's first finish our coverage of getter inputs.

In the above code, we have defined a component input property called courses, that uses internally a member variable called myCourses.

Here is how we can set the value of this input in a parent component:

@Component({
  selector: "app-parent",
  template: ` <app-child [courses]="parentCourses"/> `,
})
export class ParentComponent {

    parentCourses : Course[] = // some initial value

}

Notice that it's not possible to set the value of the myCourses property on the parent component.

You might be wondering how the name of the input property (courses) was determined.

This was determined based on the name of the getter function, which is also called courses.

@Input with aliases

It's also possible to define the name of the input property explicitly, by defining an input alias.

Here is how to do it using a simplified notation:

@Component({
  selector: "app-child",
  template: ` <p>{{ name }}</p> `,
})
export class ChildComponent {
  @Input("userName") name: string;
}

In the above code, the name input property is aliased to userName.

With the alias in place, this is how you can set the property in a parent component:

@Component({
  selector: "app-parent",
  template: ` <app-child [userName]="name" />`,
})
export class ParentComponent {
  name = "John Doe";
}

Notice that if you are using the alias option in combination with other input options, you can also define your alias using the @Input configuration object:

@Component({
  selector: "app-child",
  template: ` <p>{{ name }}</p> `,
})
export class ChildComponent {
  @Input({
    alias: "userName",
    required: true,
  })
  name: string;
}

@Input transforms

As we have mentioned, a better alternative to using input getters and setters is to instead use input transforms.

With input transforms we can easily modify the value of an input value before assigning it to the property of the component, essentially achieving the same effect as what we just did by using a setter.

This is done by using the transform property of the @Input decorator:

@Component({
  selector: "app-child",
  template: ` <p>{{ name }}</p> `,
})
export class ChildComponent {
  @Input({
    transform: (value: string) => value.toUpperCase(),
  })
  name: string;
}

With this transform in place, any string passed to the name input will be converted to upper case instantly.

Points to know when creating an input transform:

  • Transform function should be pure. This means that the function should not have side effects.
  • Transform functions should be fast, they should not contain heavy calculations.
  • Transform functions cannot be set conditionally, but you can add logical conditions inside the function if you need to.

Built-in input transforms booleanAttribute and numberAttribute

Besides allowing us to replace the use of getters and setters, input transforms also come in handy to support common use cases like boolean or constant numeric properties.

Angular provides us with two built-in input transforms that are useful in many common scenarios: booleanAttribute and numberAttribute.

These transforms can help to make our templates more readable in certain situations.

For example, sometimes we might want to create a boolean input property such as a disabled flag:

@Component({
  selector: "app-child",
  template: ` <p>{{ name }}</p> `,
})
export class ChildComponent {
  @Input()
  disabled: boolean;
}

But the problem with this is that to set this flag, we need to use an input expression (using []) in the parent component:

@Component({
  selector: "app-parent",
  template: ` <app-child [disabled]="true" /> `,
})
export class ParentComponent {}

This works, but wouldn't it be much more convenient if we could set the disabled flag like this instead:

@Component({
  selector: "app-child",
  template: ` <app-child disabled /> `,
})
export class ParentComponent {}

This is just a small detail, but it does feel a lot more natural to the users of your component.

In this example, the mere presence of the disabled attribute indicates that the disabled property should be set to true.

I think that this makes the code look more readable, it looks much more just like plain HTML.

By default, this simplified syntax without using a [] expression wouldn't work as expected.

But we can make this work by applying the booleanAttribute input transform:

@Component({
  selector: "app-child",
  template: ` <p>{{ name }}</p> `,
})
export class ChildComponent {
  @Input({
    transform: booleanAttribute,
  })
  disabled: boolean;
}

Now our disabled input property works as expected: the mere presence of the disabled property (even without a value) will cause the disabled property to be true, or false otherwise.

Notice that if you need to use a [] expression to set this propertly dynamically, everything will still work correctly.

The numberAttribute built-in input transform

Let's now look at numberAttribute, which is another built-in input transform.

The numberAttribute transform converts the string value of the input into a numeric value:

@Component({
  selector: "app-child",
  template: ` <p>{{ age }}</p> `,
})
export class ChildComponent {
  @Input({
    transform: numberAttribute,
  })
  age: number;
}

With this transform in place, we can now set the age property in the following way:

@Component({
  selector: "app-parent",
  template: ` <app-child age="20" />`,
})
export class ParentComponent {}

Any string that we set as the age property will be automatically converted to a number, or to NaN if the conversion is not possible.

If we didn't have this transform in place, we would have to use a slightly more verbose syntax:

@Component({
  selector: "app-parent",
  template: ` <app-child [age]="20" />`,
})
export class ParentComponent {}

This would also work, but it just feels a bit overkill just to set a simple number constant.

Using a transform makes our parent component template a bit easier to read, and makes it look closer to plain HTML.

@Input vs @Attribute: what are the differences?

Another decorator that you might come across that looks very similar to @Input is the @Attribute decorator.

The @Attribute decorator has usually been used to support certain use cases, most of which I think are now better handled via a transform.

The two decorators may look similar, but they are meant to be used for very different purposes.

To understand why both decorators exist, it's important to bear in mind how the DOM works in general.

Let's remember that a DOM element has two different sets of key-value pairs:

  • DOM attributes: These are the HTML markup attributes, they are always strings, and they don't change their value over time.

  • DOM properties: These are the properties of the actual DOM node, so they can be of any type, not just a string. Also, properties change over time, just like the property of any Javascript object.

Notice that there are certain property/attribute pairs that the DOM automatically keeps in sync, like the value property.

But that is not the case by default. In general, you can have a DOM property and a DOM attribute with the same name, and they will not be kept in sync.

How to use @Attribute

Most of the time when using a framework like Angular, we are always using DOM properties and not attributes.

We rarely need to deal directly with DOM attributes in the context of a web framework, but they are still a fundamental part of the DOM.

So just in case we ever need to do so, Angular has provided us with a decorator to grab the string value of an attribute:

Here is an example of how to use @Attribute:

@Component({
  selector: "app-child",
  template: ` <p>{{ age }}</p> `,
})
export class ChildComponent {
  constructor(@Attribute("age") public age: string) {}
}

As you can see, the @Attribute decorator is meant to be used in the constructor only.

Here is how we can set the attribute:

@Component({
  selector: "app-parent",
  template: ` <app-child age="10" /> `,
})
export class ParentComponent {}

This will inject the string "10" in the constructor of the child component.

This value is constant and won't change over time, as it's a DOM attribute.

So if you ever need by some reason to grab the value of a DOM attribute, this is how we can do it in Angular.

When to use @Input vs @Attribute?

In general, we should rely on properties and not DOM attributes, so for the vast majority of situations you should always use @Input.

If what you were looking for to do with @Attribute was mostly to support a simplified syntax for boolean and numeric values, you can use input transforms instead as we have seen.

With the introduction of input transforms, I think that there are now fewer situations where you would need the @Attribute decorator.

I hope that you enjoyed this post, to get notified when new similar posts like this come out covering Angular, I invite you to subscribe to our newsletter:

You will also get up-to-date news on the Angular ecosystem.

And if you want a deep dive into all the features of Angular Core like @Input and others, have a look at the Angular Core Deep Dive Course:

Summary

In this guide, we explored the @Input decorator in detail, covering all its currently available options.

One of the most interesting options is the input transforms feature, which allows us to make our templates more readable by supporting common use cases like boolean or constant numeric properties.

Input transforms make the use of input getters/setters and the use of @Attribute decorator less necessary than before.

Transforms are a very convenient feature, so I encourage you to start using them in your templates.

If you have any questions or comments, let me know in the comment section below. I'll be happy to help!