DEV Community

Cover image for Say Goodbye to EventEmitter: Switch to output() for Custom Events in Angular
bytebantz
bytebantz

Posted on

Say Goodbye to EventEmitter: Switch to output() for Custom Events in Angular

In Angular, components can communicate with each other by raising events. For example, when a user clicks a button inside a component, you might want to notify its parent component about that action.

To do that, Angular allows us to define custom events using the @Output decorator and EventEmitter (old way) or the output() function (new way).

This enables you to create events that can be emitted and handled by parent components, similar to native DOM events.

Old Way: Using @Output and EventEmitter

The traditional approach is as follows:

  1. You define an event using the @Output decorator and an instance of EventEmitter.
  2. You emit the event using .emit().

New Way: Using the output Function

Now, Angular introduces a simplified syntax to define custom events without needing EventEmitter.

Instead of using @Output() and EventEmitter, you can use the output() function directly.

The newer approach eliminates the need to explicitly instantiate an EventEmitter, which simplifies code and reduces boilerplate.

Even though the syntax is different, the behavior is exactly the same as the old approach. You still emit events, pass data with them, and listen for them from the parent component.

Basic Setup

To create a custom event, you define a property in your component using the output function, which returns an OutputEmitterRef.

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

@Component({
  selector: 'child-component',
  template: `<button (click)="sendData()">Click Me</button>`,
})
export class ChildComponent {
  dataSent = output<string>(); // No need to import EventEmitter

  sendData() {
    this.dataSent.emit('Hello, Parent!');
  }
}
Enter fullscreen mode Exit fullscreen mode

In this case, the output function does exactly what EventEmitter did, but in a more compact form.

Whether you’re using the old or new way, the way you listen for the event in the parent component is the same.

In the parent component, you can listen for the dataSent event using the regular event binding syntax (eventName) and call a method when the event is triggered.

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

@Component({
  selector: 'app-parent',
  template: `
    <h1>Parent Component</h1>
    <child-component (dataSent)="receiveData($event)"></child-component>
    <p>{{ receivedMessage }}</p>
  `,
})
export class ParentComponent {
  receivedMessage: string = '';

  // Method to handle the received data from the child component
  receiveData(message: string) {
    this.receivedMessage = message; // Store the message received from the child
  }
}
Enter fullscreen mode Exit fullscreen mode

Customizing Output Names

You can specify a custom name for the event in the template using the alias option

changed = output({alias: 'valueChanged'});
Enter fullscreen mode Exit fullscreen mode

· The alias option only affects how the event is referenced in the template, not in TypeScript code.

· In the TypeScript code, the original name of the output property is used (in this case, changed).

· In the template, the alias name valueChanged is used to listen for the event.

Subscribing to Outputs Programmatically

When dynamically creating components in Angular, you might want to listen for events emitted by the component, for example, when a button inside the dynamically created component is clicked.

You can do this by subscribing to their output events programmatically using subscribe.

After creating the dynamic component in the parent component, we can subscribe to its event property to listen for it.

When the event is emitted, the subscription in the parent component’s code will receive the emitted data and can take an action based on it, such as logging it to the console, updating the UI, or performing some other logic.

const componentRef = viewContainerRef.createComponent(SomeComponent);

// Subscribe to the dynamically created component's output event
componentRef.instance.someEventProperty.subscribe((eventData) => {
  console.log(eventData); 
});
Enter fullscreen mode Exit fullscreen mode

You can also unsubscribe manually:

const eventSubscription = componentRef.instance.someEventProperty.subscribe(eventData => {
  console.log(eventData);
});

eventSubscription.unsubscribe();
Enter fullscreen mode Exit fullscreen mode

Key Points about Custom Events

  • Custom events do not bubble: In native DOM events, when an event is triggered, it can propagate up from the target element to its ancestors, allowing parent elements to listen for events on child elements. However, custom events in Angular do not bubble.

  • Case sensitivity: Output names are case-sensitive.

  • Inheritance: When a component extends another, it inherits the outputs from the parent component. This means child component automatically inherits the output (custom event) from the parent and can listen to or trigger it without needing to redefine the event.

  • Event names: Choose event names carefully to avoid conflicts with native DOM event names like click, focus, etc.

Conclusion

In conclusion, Angular provides two approaches for component communication through custom events: the traditional method using @Output() and EventEmitter, and the newer, simpler output() function. The newer approach eliminates the need for importing EventEmitter and reduces boilerplate code, making the code more concise and easier to maintain.

Check out my deep dives on → Gumroad

Top comments (0)