Custom component events are a crucial feature of any web framework.

And in Angular, we can create component events by using the @Output decorator alongside EventEmitter.

In this guide, we will learn how to use @Output to emit custom component events, and talk about a common confusion that you might step into while designing component events.

Table of Contents

This post covers the following topics:

  • What is @Output?
  • Basic Syntax of @Output
  • What data type can @Output emit?
  • Parent-Child data communication using @Output
  • Accessing emitted data in the template
  • Is it a must to pass the $event to the handler method?
  • Must the name be $event in the template?
  • @Output with aliases
  • How to set outputs in the @Component decorator
  • How to set output alias in the @Component decorator
  • What type of data type can you bind to @Output?
  • Can the @Output be used to broadcast events to multiple components?
  • How often can I use @Output?
  • How can I specify the type of data emitted by an @Output?
  • Must @Output be used with EventEmitter?
  • Do the @Ouput and @Input have to be in the same component?
  • Do the custom events bubble up to the DOM?
  • What happens to the outputs when a component is extended?
  • Are output names case-sensitive?
  • Common mistakes when using @Output
  • Summary

What is @Output?

@Output is a decorator that marks a component property as an output of the component.

It is a mechanism that allows an Angular component to report output events to its parent components.

The @Output() must be associated with an EventEmitter.

When an EventEmitter emits an event, any parent component listening to that event through an event binding can respond to it accordingly.

Basic Syntax of @Output

The basic syntax of @Output is as follows:

@Output({
    alias?: string;
}) propertyName = new EventEmitter<type>();

alias: The name of the DOM property to which the output property is bound.

propertyName: The name of the property that emits events. This is a class field that is decorated with @Output. If we don't specify this, this will be the property name itself.

type: This generic parameter defines the data type of the event payload. The consumers of the event will receive an instance of this type.

Here's an example of how @Output() is used:

@Component({
  selector: "app-child",
  template: ` <button (click)="emitEvent()">
  Send Data to Parent
  </button> `,
})
export class ChildComponent {

  @Output() 
  myEvent = new EventEmitter<string>();

  emitEvent() {
    this.myEvent.emit("Hello World!");
  }
}

In the above example, the ChildComponent has a property myEvent which is decorated with @Output.

With this code, anytime that we want to report an event of type myEvent to the outside world, we can call the emitEvent method.

The parent component needs then to subscribe to the output event and react to it.

Here's how the parent component's template might look:

@Component({
  selector: "app-parent",
  template: ` 
  <app-child (myEvent)="handleEvent($event)" />`,
})
export class ParentComponent {
  handleEvent(event: string) {
    console.log(event);
  }
}

The ParentComponent renders the app-child, and wants to get notified whenever the event myEvent occurs.

So it uses the () event syntax to bind to the myEvent event and assigns a handler method to it named handleEvent.

This handleEvent method gets called whenever the myEvent.emit() is called inside the child component.

The handleEvent method then receives the data emitted by the myEvent event.

Notice the use of the $event special variable. This variable contains the data passed to the emit() call.

So in this way, the child component has communicated with the parent component in a decoupled, event-driven way.

@Output with aliases

By default, the name of the @Output() property becomes also the name of the custom event. But for some reason, you might want to use a different name.

You can do so by supplying an alias to the @Output decorator:

@Output('customEvent') 
myEvent = new EventEmitter<string>();

The customEvent name will now be used to bind to the event of the child component:

@Component({
  selector: "app-parent",
  template: ` 
  <app-child (customEvent)="handleEvent($event)" />`,
})
export class ParentComponent {
  handleEvent(event: string) {
    console.log(event);
  }
}

If we try to use the previous (myEvent) event-binding syntax, it will throw an error.

Avoid a common mistake when using @Output

This mistake happens very frequently, and it can lead to code that is hard to maintain.

The @Output mechanism is meant to report custom events, but it's not meant to be used to explicitly trigger actions in other components.

The difference is perhaps subtle the first time you hear it, but it's crucial to understand it.

You see, these are two very different concepts, that we cannot mix together:

  • Event: it reports an internal change of state of the component
  • Command: it tells another component explicitly to do a very specific action

The difference is subtle but crucial.

When we send a command, we have a lot of information about the receiver of the command and what it's going to do in reponse to the command.

But that is not the case when talking about events.

Let's see an example of how this could occur and why that would be problematic:

@Component({
  selector: "child",
  template: ` <button (click)="onClick()">
  Trigger logic
  </button> `,
})
export class ChildComponent {

  @Output()
  updateTotalCoursesCounter = new EventEmitter<>();

  onClick() {
    if (someBusinessLogic) {
      this.updateTotalCoursesCounter.emit();
    }
  }
}

Notice the name of this output: it's not an event, but a command.

This output tells some other external component outside of this child component to perform an explicit action.

The problem with this design is that this child component has way too much information about the internal workings of another completely unrelated component.

The placement of the business logic is likely at the wrong place in the application. It probably shouldn't be inside the child component.

Let's switch this to an event output instead:

@Component({
  selector: "child",
  template: ` <button (click)="onClick()">
  Trigger logic
  </button> `,
})
export class ChildComponent {
  @Output()
  somethingHappened = new EventEmitter<>();

  onClick() {
    this.somethingHappened.emit();
  }
}

Notice that here, we are just reporting to the outside of the component that something happened, but we are not telling the outside world what to do.

We are just reporting an internal change of state of the component, and it's for the outside world to decide what to do in response to that, if anything at all.

This way, the child component is not aware of what other components in the application are interested in this event, and the child component does not know what the other components will do with that information.

The child component makes no assumptions about how, where, when or if this custom event will be used.

It is up to the consumers of this output to decide what to do in response to the event:

@Component({
  selector: "app-parent",
  template: ` 
  <app-child (somethingHappened)="handleEvent()" /> `,
})
export class ParentComponent {
  handleEvent() {
    if (someBusinessLogic) {
      // update the total courses counter
    }
  }
}

Here the parent component detected the event and did something in response to it. The child component does not which parent component captured the event, or what it did in response to it.

Notice the placement of the business logic: it's now in the right place.

It would have been surprising to find this business logic inside the child component, as that's not where it belongs.

The main takeaway on how to avoid the Event vs Command design pitfall

When designing your component outputs, remember the rule that an event is not a command, and that @Output is meant to report events, not to trigger actions explicitly in other components.

One good way of making sure that you are following this rule is to carefully inspect the name of the output.

If it's not a verb in past tense, check to see if the output is not being misused to trigger an action that the component should not know about.

A verb in the past tense is a good indicator that the output is just reporting an internal change in the component state, and not telling the outside world what to do.

Let's now cover some common FAQs related to the use of Output.

FAQs about @Output and EventEmitter

Here are some of the most frequently asked questions about @Output and
EventEmitter.

What data type can @Output emit?

The @Output EventEmitter can emit absolutely any data type.

It can emit primitive data types, objects or arrays:

// primitive values.
this.myEvent.emit("Hello World!");
this.myEvent.emit(90);
this.myEvent.emit(true);

// You can emit custom event objects
this.myEvent.emit({
  name: "Chidume Nnamdi",
  age: 90,
});

this.myEvent.emit([1, 2, 3, "4", true]);

const person = Person();
this.myEvent.emit(person);

Is it a must to pass the $event to the handler method?

No, it isn't. You can still omit it even if the data type is not void.

@Component({
  selector: "app-parent",
  template: ` 
  <app-child (myEvent)="handleEvent()" /> `,
})
export class ParentComponent {
  handleEvent() {
    console.log("Hello World!");
  }
}

Can the @Output be used to broadcast events to multiple components?

@Output() is intended for direct communication between the child component and the parent component. It's not possible or intended to use @Output() to broadcast events to multiple components.

Must @Output be used with EventEmitter?

The @Output must always be an instance of EventEmitter.

Do the custom events bubble up to the DOM?

No, the custom events do not bubble up to the DOM, unlike most standard browser events.

Is manually bubbling custom events up the component tree a good idea?

No, this is an anti-pattern. Use instead a shared service to communicate between components that don't have a direct parent-child relationship.

What happens to the outputs when a component is extended?

When a component is extended, the child component inherits the outputs of the parent component.

Are output names case-sensitive?

Yes, output names are case-sensitive. The name of the output property must match the name of the class field. Any mismatch at all will result in an error.

How to set outputs in the @Component decorator

Without using the @Output decorator, we can still set output properties for a component.

This is an alternative syntax that might come in handy in certain cases:

@Component({
  selector: "app-child",
  template: ` <button (click)="emitEvent()">
  Send Data to Parent
  </button> `,
  outputs: ["myEvent"],
})
export class ChildComponent {

  myEvent = new EventEmitter<string>();

  emitEvent() {
    this.myEvent.emit("Hello World!");
  }
}

How to set output alias in the @Component decorator

We do this by separating the names with a colon :. The name on the left-hand side is the name of the class field, while the name on the right-hand side is the alias name:

@Component({
  selector: "app-child",
  template: ` <button (click)="emitEvent()">
  Send Data to Parent
  </button> `,
  outputs: ["myEvent: customEvent"],
})
export class ChildComponent {
  myEvent = new EventEmitter<string>();

  emitEvent() {
    this.myEvent.emit("Hello World!");
  }
}

I hope that you enjoyed this post, to get notified when new similar posts like this are out, 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 @Output and others, have a look at the Angular Core Deep Dive Course:

Summary

In this guide, we went deep into the depths of the @Output decorator.

Besides the basic syntax and the features that it provides, we have learned that @Output is not meant to be explicitly used to trigger actions in other components but to report custom events that represent internal changes in the state of the component.

A good rule of thumb to follow is to check the output name and make sure that it's a verb in the past tense.

If that is the case, that is a good indication that the output mechanism is being used in a maintainable way.

If you have any questions or comments, let me know as well. I'll be happy to help!