ListContainer

The <smart-list-container> component is a presentational layout wrapper that groups list items with a semantic role="list". It follows the Base + Standard + Wrapper pattern with an InjectionToken-based extension mechanism. The abstract ListContainerBaseComponent defines the shared API — optional IListContainerOptions and cssClass (alias class). ListContainerStandardComponent is a barebones placeholder concrete implementation that projects content via <ng-content /> inside a role="list" div with an optional data-variant attribute. ListContainerComponent is the public wrapper that renders ListContainerStandardComponent by default and accepts a custom replacement via LIST_CONTAINER_STANDARD_COMPONENT_TOKEN.


Usage

<smart-list-container [options]="options">
  @for (notification of notifications; track notification.id) {
    <div role="listitem">
      <p>{{ notification.text }}</p>
      <small>{{ notification.time }}</small>
    </div>
  }
</smart-list-container>

Components

ListContainerComponent (<smart-list-container>)

Main wrapper component. Renders ListContainerStandardComponent by default. When LIST_CONTAINER_STANDARD_COMPONENT_TOKEN is provided, renders the injected component via NgComponentOutlet. Supports content projection through <ng-content /> in the default branch, so children placed inside <smart-list-container> are rendered inside the standard list container.

ListContainerStandardComponent (<smart-list-container-standard>)

Barebones placeholder concrete implementation. Renders a <div role="list"> that:

  • exposes the variant value via data-variant (omitted when options is not provided),
  • applies the external cssClass directly on the host div,
  • projects all children via <ng-content />.

It does not include Tailwind UI styling — it exists solely as the default structural placeholder until a custom implementation is registered through the token.

ListContainerBaseComponent (abstract)

Abstract base directive for extending custom list container implementations. Exposes options as an InputSignal<IListContainerOptions | undefined> and cssClass as an InputSignal<string> (with alias class). Has no outputs or methods.

API

Inputs

InputTypeDefaultDescription
optionsInputSignal<IListContainerOptions | undefined>-Optional configuration (variant, fullWidthOnMobile)
classInputSignal<string>''External CSS classes (alias for cssClass)

IListContainerOptions

The standard component only consumes variant (placeholder behavior — applied as the data-variant attribute on the list root). The fullWidthOnMobile flag is reserved for custom implementations registered through LIST_CONTAINER_STANDARD_COMPONENT_TOKEN and is ignored by ListContainerStandardComponent.

LIST_CONTAINER_STANDARD_COMPONENT_TOKEN

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

Extending the base class

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

import {
  IListContainerOptions,
  LIST_CONTAINER_STANDARD_COMPONENT_TOKEN,
  ListContainerBaseComponent,
  ListContainerComponent,
} from '@smartsoft001/angular';

interface DocsTeamMember {
  name: string;
  role: string;
}

/**
 * A custom list container built on `ListContainerBaseComponent`.
 *
 * The base contributes the `options` and `class` inputs; the implementation
 * decides how the rows are framed and separated.
 */
@Component({
  selector: 'docs-custom-list-container',
  template: `
    <ul role="list" [class]="containerClasses()">
      <!--
        smart-list-container renders a custom implementation through
        NgComponentOutlet, which does not forward projected content. Only
        options and cssClass arrive here, so a custom container owns its rows
        instead of relying on ng-content.
      -->
      @for (member of members; track member.name) {
        <li class="docs-list-container__item">
          <span class="docs-list-container__name">{{ member.name }}</span>
          <span class="docs-list-container__role">{{ member.role }}</span>
        </li>
      }
    </ul>
  `,
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomListContainerComponent extends ListContainerBaseComponent {
  // NgComponentOutlet passes 'cssClass' by canonical name, not the 'class' alias.
  override cssClass = input<string>('');

  members: DocsTeamMember[] = [
    { name: 'Lindsay Walton', role: 'Front-end Developer' },
    { name: 'Courtney Henry', role: 'Designer' },
    { name: 'Tom Cook', role: 'Director of Product' },
  ];

  containerClasses = computed(() => {
    const classes = ['docs-list-container'];
    const variant = this.options()?.variant;
    if (variant) classes.push(`docs-list-container--${variant}`);
    if (this.options()?.fullWidthOnMobile) {
      classes.push('docs-list-container--full-width-mobile');
    }
    const extra = this.cssClass();
    if (extra) classes.push(extra);
    return classes.join(' ');
  });
}

/**
 * Registering the implementation against
 * `LIST_CONTAINER_STANDARD_COMPONENT_TOKEN` makes every
 * `<smart-list-container>` in this injector render it instead of the standard
 * variation.
 */
@Component({
  selector: 'docs-list-container-custom-example',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [ListContainerComponent],
  providers: [
    {
      provide: LIST_CONTAINER_STANDARD_COMPONENT_TOKEN,
      useValue: CustomListContainerComponent,
    },
  ],
  template: `<smart-list-container [options]="options" />`,
})
export class ListContainerCustomExampleComponent {
  options: IListContainerOptions = {
    variant: 'separate-cards',
    fullWidthOnMobile: true,
  };
}

Source

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