Naren Murali
Naren Murali

Reputation: 57986

I want to share a signal between components aross my application, alternatives to service implementation

My scenario is, I have a dialog, which is opened through a signal, but I want to trigger this dialog to open, across my application. The obvious way is to create a DialogService (providedIn: 'root') and store the signal there, but is there a more lightweight way to achieve the same.

Dialog component HTML:

@if(showDialog()) {
  <kendo-dialog 
    title="sometitle"
  >
    some text
  </kendo-dialog>
}

Dialog Component TS:

@Component({ ... })
export class DialogComponent {
  showDialog = signal(false); // <-- I want to share this signal, across multiple components.
}

Version is Angular 19 but am ok with something that is backwards compatible also.

Upvotes: 4

Views: 96

Answers (1)

Naren Murali
Naren Murali

Reputation: 57986

For this solution you can look at the source code of angular.dev, they have a search popup (for searching documentation), this dialog component is opened/closed, from a signal, that is actually provided using an InjectionToken (providedIn: 'root' -> provided throughout the application), this token, thanks to Dependency injection can be shared across components easily and the dialog can be opened from anywhere.

app.component.ts - Adev folder angular Github Source Code Reference

Below is how we define a injection token signal:

export const DIALOG_OPEN = new InjectionToken('DIALOG_OPEN', {
  providedIn: 'root',
  factory: () => signal(false),
});

Then we define the DialogCustomComponent to open based on this DI token.

@Component({
  selector: 'app-dialog',
  imports: [DialogComponent, DialogActionsComponent],
  template: `
  @if(opened()) {
  <kendo-dialog title="Oh no!" (close)="close()">
    <p style="margin: 30px; text-align: center;">Dialog was opened.</p>

    <kendo-dialog-actions>
      <button kendoButton (click)="close()" themeColor="primary">
        Close
      </button>
    </kendo-dialog-actions>
  </kendo-dialog>
  }
  `,
})
export class DialogCustomComponent {
  opened: WritableSignal<boolean> = inject(DIALOG_OPEN);
  close() {
    this.opened.update((prev: boolean) => !prev);
  }
}

For example, if we want to open the dialog, from the root component, just inject the token and toggle it for the dialog to open.

@Component({
  selector: 'my-app',
  template: `
    <button kendoButton (click)="open()">Show Dialog</button>
    <button kendoButton (click)="close()">Close Dialog</button>
    <app-dialog/>
  `,
  standalone: false,
})
export class AppComponent {
  opened: WritableSignal<boolean> = inject(DIALOG_OPEN);

  public close(): void {
    this.opened.set(false);
  }

  public open(): void {
    this.opened.set(true);
  }
}

Full Code:

import {
  Component,
  inject,
  InjectionToken,
  signal,
  WritableSignal,
} from '@angular/core';
import {
  DialogComponent,
  DialogActionsComponent,
} from '@progress/kendo-angular-dialog';

export const DIALOG_OPEN = new InjectionToken('DIALOG_OPEN', {
  providedIn: 'root',
  factory: () => signal(false),
});

@Component({
  selector: 'app-dialog',
  imports: [DialogComponent, DialogActionsComponent],
  template: `
  @if(opened()) {
  <kendo-dialog title="Oh no!" (close)="close()">
    <p style="margin: 30px; text-align: center;">Dialog was opened.</p>

    <kendo-dialog-actions>
      <button kendoButton (click)="close()" themeColor="primary">
        Close
      </button>
    </kendo-dialog-actions>
  </kendo-dialog>
  }
  `,
})
export class DialogCustomComponent {
  opened: WritableSignal<boolean> = inject(DIALOG_OPEN);
  close() {
    this.opened.update((prev: boolean) => !prev);
  }
}

@Component({
  selector: 'my-app',
  template: `
    <button kendoButton (click)="open()">Show Dialog</button>
    <button kendoButton (click)="close()">Close Dialog</button>
    <app-dialog/>
  `,
  standalone: false,
})
export class AppComponent {
  opened: WritableSignal<boolean> = inject(DIALOG_OPEN);

  public close(): void {
    this.opened.set(false);
  }

  public open(): void {
    this.opened.set(true);
  }
}

Stackblitz Demo

Upvotes: 6

Related Questions