Toggle

The <smart-toggle> component provides a boolean on/off control. It follows the Base + Standard + Wrapper pattern with an InjectionToken-based extension mechanism. The abstract ToggleBaseComponent defines the shared API — value (two-way ModelSignal<boolean>), disabled, optional IToggleOptions, cssClass (alias class), and a toggle() method that flips the value while respecting the disabled state. ToggleStandardComponent is a barebones placeholder concrete implementation. ToggleComponent is the public wrapper that renders ToggleStandardComponent by default and accepts a custom replacement via TOGGLE_STANDARD_COMPONENT_TOKEN.


Usage

<smart-toggle [options]="options" [(value)]="emailNotifications" />

Components

ToggleComponent (<smart-toggle>)

Main wrapper component. Renders ToggleStandardComponent by default. When TOGGLE_STANDARD_COMPONENT_TOKEN is provided, renders the injected component via NgComponentOutlet.

ToggleStandardComponent (<smart-toggle-standard>)

Barebones native-HTML implementation. Renders a <span class="smart-toggle" data-label-position="…"> containing a minimal <input type="checkbox"> bound to value and disabled, with the external cssClass applied to the input element. options.label renders in a <label class="smart-toggle-label" for="…"> associated with the checkbox (so it is the accessible name), and options.description in a <span class="smart-toggle-description"> referenced by the checkbox's aria-describedby. Both live in a <span class="smart-toggle-text" data-role="text"> placed after the checkbox, or before it when options.labelPosition === 'left' (default 'right'). options.ariaLabel is applied as the checkbox's aria-label only when there is no label. It does not include Tailwind UI styling.

TogglePresetComponent (<smart-toggle-preset>)

Styled variation that extends ToggleBaseComponent and is a drop-in replacement for ToggleStandardComponent. Register it via TOGGLE_STANDARD_COMPONENT_TOKEN to restyle every <smart-toggle>, or use the <smart-toggle-preset> selector directly. It renders the Preline default switch: a hidden, accessible <input type="checkbox"> (with peer sr-only) drives the track / thumb visuals through peer-checked / peer-disabled states, while the value model holds the checked state (updated via the checkbox change event). Honors options.label and options.description (rendered beside the switch), options.labelPosition ('left' | 'right', default 'right'), options.ariaLabel (forwarded to the checkbox), and disabled. All classes are smart:-prefixed Tailwind with explicit dark: variants. The class recipes live in preset/preset-classes.util.ts (getToggleContainerClasses, getToggleSwitchClasses, getToggleTrackClasses, getToggleThumbClasses, getToggleTextWrapClasses, getToggleLabelClasses, getToggleDescriptionClasses).

Because ToggleComponent renders injected components via NgComponentOutlet (which passes inputs by canonical name), TogglePresetComponent overrides cssClass as input<string>('') without the class alias. Bind it as [cssClass] when using the <smart-toggle-preset> selector directly, or just pass class on <smart-toggle> (the wrapper forwards it). Note that the Preline doc's size, soft-color, rounded, icon, tooltip, and validation-state variants are not exposed, because IToggleOptions has no size/variant/color fields; the preset renders the default medium pill switch in the primary (blue) color.

ToggleBaseComponent (abstract)

Abstract base directive for extending custom toggle implementations. Exposes value as a two-way ModelSignal<boolean> (default false), disabled as an InputSignal<boolean> (default false), options as an InputSignal<IToggleOptions | undefined>, cssClass as an InputSignal<string> (with alias class), and a toggle() method that calls value.set(!value()) when disabled() is false.

API

Inputs

InputTypeDefaultDescription
valueModelSignal<boolean>falseToggle on/off state (two-way bindable)
disabledInputSignal<boolean>falseWhether the toggle is disabled
optionsInputSignal<IToggleOptions | undefined>-Optional configuration (label, description, ariaLabel, position)
classInputSignal<string>''External CSS classes (alias for cssClass)

IToggleOptions

Both ToggleStandardComponent and TogglePresetComponent honour every property: label (the checkbox's accessible name — a <label for> in the standard), description (referenced by aria-describedby in the standard), labelPosition ('left' puts the text before the switch, otherwise it goes after — default 'right') and ariaLabel (the standard uses it only when there is no visible label; set it for label-less toggles).

TOGGLE_STANDARD_COMPONENT_TOKEN

InjectionToken that allows replacing the default ToggleStandardComponent with a custom implementation. Provide a Type<ToggleBaseComponent> to override.

Extending the base class

import {
  ChangeDetectionStrategy,
  Component,
  computed,
  input,
  ViewEncapsulation,
} from '@angular/core';

import {
  IToggleOptions,
  TOGGLE_STANDARD_COMPONENT_TOKEN,
  ToggleBaseComponent,
  ToggleComponent,
} from '@smartsoft001/angular';

@Component({
  selector: 'docs-custom-toggle',
  template: `
    <label [class]="containerClasses()">
      @if (options()?.labelPosition === 'left' && options()?.label) {
        <span class="docs-toggle__label">{{ options()!.label }}</span>
      }

      <input
        type="checkbox"
        class="docs-toggle__input"
        [checked]="value()"
        [disabled]="disabled()"
        [attr.aria-label]="options()?.ariaLabel ?? null"
        (change)="onChange($event)"
      />

      @if (options()?.labelPosition !== 'left' && options()?.label) {
        <span class="docs-toggle__label">{{ options()!.label }}</span>
      }
      @if (options()?.description) {
        <span class="docs-toggle__description">{{
          options()!.description
        }}</span>
      }
    </label>
  `,
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomToggleComponent extends ToggleBaseComponent {
  // NgComponentOutlet passes 'cssClass' by canonical name, not the 'class' alias.
  override cssClass = input<string>('');

  containerClasses = computed(() =>
    [
      'docs-toggle',
      `docs-toggle--label-${this.options()?.labelPosition ?? 'right'}`,
      this.disabled() ? 'docs-toggle--disabled' : '',
      this.cssClass(),
    ]
      .filter(Boolean)
      .join(' '),
  );

  // A change handler reads the checkbox state; a click handler would instead
  // call the inherited toggle(), which already respects `disabled`.
  protected onChange(event: Event): void {
    this.value.set((event.target as HTMLInputElement).checked);
  }
}

@Component({
  selector: 'docs-toggle-custom-example',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [ToggleComponent],
  // The token swaps the standard toggle for the custom one everywhere below
  // this component, so consumers keep writing `<smart-toggle>`.
  providers: [
    {
      provide: TOGGLE_STANDARD_COMPONENT_TOKEN,
      useValue: CustomToggleComponent,
    },
  ],
  // NgComponentOutlet forwards inputs only, so the [(value)] write-back stays
  // silent here and `enabled` below is the initial state.
  template: `<smart-toggle [value]="enabled" [options]="options" />`,
})
export class ToggleCustomExampleComponent {
  enabled = false;

  options: IToggleOptions = {
    label: 'Allow notifications',
    description: 'Send me an email when someone comments on my work.',
    labelPosition: 'right',
    ariaLabel: 'Allow notifications',
  };
}

Source

The component lives in packages/shared/angular/src/lib/components/toggle and is documented for Claude Code by the angular-components-toggle skill.