Description List

The <smart-description-list> component renders a list of label/value pairs (a <dl> with <dt>/<dd> rows) with optional title, description, per-item value/action template slots, and bottom attachments/footer slots. It follows the Base + Standard + Wrapper pattern with an InjectionToken-based extension mechanism. The abstract DescriptionListBaseComponent defines the shared API — optional IDescriptionListOptions and cssClass (alias class). DescriptionListStandardComponent is a barebones placeholder concrete implementation. DescriptionListComponent is the public wrapper that renders DescriptionListStandardComponent by default and accepts a custom replacement via DESCRIPTION_LIST_STANDARD_COMPONENT_TOKEN.


Usage

<smart-description-list [options]="options()" />

<ng-template #editEmail>
  <button type="button" (click)="onEdit('email')">Update</button>
</ng-template>

Components

DescriptionListComponent (<smart-description-list>)

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

DescriptionListStandardComponent (<smart-description-list-standard>)

Barebones placeholder concrete implementation. Renders a wrapper <div> containing an optional <h3 class="title">, optional <p class="description">, and a <dl> with one <div class="item"> per item. Each item renders a <dt> (label) and a <dd> whose content is either the static value string or the valueTpl template, optionally followed by actionTpl inside <span class="action">. Bottom slots attachmentsTpl and footerTpl render in <div class="attachments"> and <div class="footer"> respectively. The external cssClass is applied to the root wrapper. It does not include any visual styling — it exists solely as the default structural placeholder until a custom implementation is registered through the token.

DescriptionListBaseComponent (abstract)

Abstract base directive for extending custom description-list implementations. Exposes options as an InputSignal<IDescriptionListOptions | undefined> and cssClass as an InputSignal<string> (with alias class).

API

Inputs

InputTypeDefaultDescription
optionsInputSignal<IDescriptionListOptions | undefined>-Optional configuration (title, description, items, attachments/footer slot templates)
classInputSignal<string>''External CSS classes (alias for cssClass)

IDescriptionListOptions

All properties are optional. The default DescriptionListStandardComponent consumes every property; a section is rendered only when its template/string is provided. Within an item, valueTpl takes precedence over value when both are set.

DESCRIPTION_LIST_STANDARD_COMPONENT_TOKEN

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

Extending the base class

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

import {
  DescriptionListBaseComponent,
  DescriptionListComponent,
  DESCRIPTION_LIST_STANDARD_COMPONENT_TOKEN,
  IDescriptionListOptions,
} from '@smartsoft001/angular';

/**
 * A custom description list built on `DescriptionListBaseComponent`.
 *
 * The base contributes the `options` and `class` inputs. Each item carries
 * either a plain `value` or a `valueTpl` template, so an implementation that
 * wants to stay compatible with the standard one projects both.
 */
@Component({
  selector: 'docs-custom-description-list',
  template: `
    <div [class]="containerClasses()">
      @if (options()?.title) {
        <h3 class="docs-description-list__title">{{ options()!.title }}</h3>
      }
      @if (options()?.description) {
        <p class="docs-description-list__description">
          {{ options()!.description }}
        </p>
      }
      <dl>
        @for (item of options()?.items ?? []; track $index) {
          <div class="docs-description-list__row">
            <dt>{{ item.label }}</dt>
            <dd>
              @if (item.valueTpl) {
                <ng-container [ngTemplateOutlet]="item.valueTpl" />
              } @else {
                {{ item.value }}
              }
            </dd>
          </div>
        }
      </dl>
    </div>
  `,
  imports: [NgTemplateOutlet],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomDescriptionListComponent extends DescriptionListBaseComponent {
  // The wrapper hands inputs to NgComponentOutlet by canonical name, so the
  // consumer's class arrives as `cssClass` rather than through the alias.
  override cssClass = input<string>('');

  containerClasses = computed(() =>
    ['docs-description-list', this.cssClass()].filter(Boolean).join(' '),
  );
}

/**
 * Registering the implementation against
 * `DESCRIPTION_LIST_STANDARD_COMPONENT_TOKEN` makes every
 * `<smart-description-list>` in this injector render it instead of the
 * standard variation.
 */
@Component({
  selector: 'docs-description-list-custom-example',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [DescriptionListComponent],
  providers: [
    {
      provide: DESCRIPTION_LIST_STANDARD_COMPONENT_TOKEN,
      useValue: CustomDescriptionListComponent,
    },
  ],
  template: `<smart-description-list [options]="options" />`,
})
export class DescriptionListCustomExampleComponent {
  options: IDescriptionListOptions = {
    title: 'Applicant information',
    description: 'Personal details and application.',
    items: [
      { label: 'Full name', value: 'Margot Foster' },
      { label: 'Application for', value: 'Backend Developer' },
      { label: 'Salary expectation', value: '$120,000' },
    ],
  };
}

Preset

DescriptionListPresetComponent (<smart-description-list-preset>) is a styled drop-in replacement for the barebones standard component. It renders the same structure with Tailwind utility classes (every class carries the smart: prefix, with explicit smart:dark:* twins):

  • Optional header (data-role="header"): title as <h3> (text-base font-semibold text-gray-900 dark:text-white) and description as <p> (text-sm text-gray-500 dark:text-gray-400). Rendered only when title or description is set.
  • List (data-role="list"): the <dl> carries divide-y divide-gray-200 dark:divide-gray-700; the external cssClass is merged onto it.
  • Rows (data-role="row"): grid py-3 sm:grid-cols-3 sm:gap-4 with <dt> (data-role="term", text-sm font-medium text-gray-500 dark:text-gray-400) and <dd> (data-role="value", text-sm text-gray-900 dark:text-white sm:col-span-2). valueTpl wins over value; actionTpl renders right-aligned inside data-role="action".
  • Optional attachmentsTpl (data-role="attachments") and footerTpl (data-role="footer") render as separate sections below the list.

Because the wrapper forwards inputs canonically through NgComponentOutlet, the preset declares override cssClass = input<string>('') (dropping the inherited class alias). Register it through the token to restyle every <smart-description-list>:

No new IDescriptionListOptions fields are introduced; the preset consumes the existing API. Class recipes live in preset/preset-classes.util.ts (not exported from the barrel).

Source

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