Site de Emmanuel Demey

A Lesser-Known Way to Share Data Between Angular Components

Angular offers multiple syntaxes to share information between components. But do you know this one?

We are used to using dependency injection with Angular in order to share information. But did you know that it is also possible to directly retrieve the parent component inside the TypeScript class of another component?

Use Case

When we need to transfer information between two indirect components, we immediately think of two solutions:

  • Setting up a service
  • Installing and configuring heavy artillery such as NgRX

I have absolutely nothing against these two solutions, but for some cases, this is not necessary. If two components are, for example, always used together for the same feature (the component split was only made to improve readability and maintainability of our code), we can, at any time, inject from a child component an instance of one of its parent components (direct or indirect).

Implementation

To achieve this, we will use the same syntax as for service injection: by defining a new parameter in the component’s constructor. If you are familiar with Angular’s mechanics, you already know that Angular relies on the parameter’s type to determine which object should be injected. In the case presented in this article, the type will simply be the one of the component(s) we want to inject.

For example, if we have a TabComponent in which we want to inject the parent TabsComponent, we just need to write the following code:

@Component({ ...}}
export class TabComponent {
    constructor(private list: TabsComponent) {}
}

Once this injection is done, we can use the methods and variables of the TabsComponent instance. This practice is notably used in the Accordion component of the ng-bootstrap library. In fact, in the NgPanelToggle directive (responsible for opening and closing the displayed content), the Accordion component itself is injected so that its toggle method can be called when the user clicks the button.

@Directive({
  selector: "button[ngbPanelToggle]",
  host: {
    type: "button",
    "[disabled]": "panel.disabled",
    "[class.collapsed]": "!panel.isOpen",
    "[attr.aria-expanded]": "panel.isOpen",
    "[attr.aria-controls]": "panel.id",
    "(click)": "accordion.toggle(panel.id)",
  },
})
export class NgbPanelToggle {
  static ngAcceptInputType_ngbPanelToggle: NgbPanel | "";

  @Input()
  set ngbPanelToggle(panel: NgbPanel) {
    if (panel) {
      this.panel = panel;
    }
  }

  constructor(
    public accordion: NgbAccordion,
    @Optional() @Host() public panel: NgbPanel,
  ) {}
}

This is my very first article on Angular (actually the very first article of this blog). More articles are planned, so feel free to come back and read them.