Notifications

A notification feed with dismissible items, unread badges, and clear-all action.

Preview

Notifications5
🎉
Welcome!Your account has been created successfully.2 min ago

💬
New commentAlice replied to your post.15 min ago

New followerBob started following you.1 hour ago

🔔
ReminderYour subscription renews tomorrow.3 hours ago

📦
Update availableng-oat v2.1 is now available.1 day ago

html
<div class="max-w-lg">
  <div class="card">
    <header class="hstack justify-between">
      <div class="hstack gap-2">
        <strong>Notifications</strong>
        <ng-oat-badge variant="secondary">{{ notifications().length }}</ng-oat-badge>
      </div>
      <button class="btn ghost small" (click)="clearAll()">Clear all</button>
    </header>
    <div class="vstack gap-1">
      @for (item of notifications(); track item.id) {
        <div class="hstack gap-3 items-start p-2">
          <span class="text-3">{{ item.icon }}</span>
          <div class="vstack gap-1 flex-1">
            <strong class="text-small">{{ item.title }}</strong>
            <span class="text-small text-light">{{ item.description }}</span>
            <small class="text-light">{{ item.time }}</small>
          </div>
          <button class="btn ghost small" (click)="dismiss(item.id)">✕</button>
        </div>
        <hr />
      } @empty {
        <p class="text-center text-light py-4">No notifications</p>
      }
    </div>
  </div>
</div>

SFC Source

typescript
import { Component, signal } from '@angular/core';
import { NgOatBadge } from '@letsprogram/ng-oat';

@Component({
  selector: 'app-notifications',
  imports: [NgOatBadge],
  template: `
    <div class="max-w-lg">
      <div class="card">
        <header class="hstack justify-between">
          <div class="hstack gap-2">
            <strong>Notifications</strong>
            <ng-oat-badge variant="secondary">{{ notifications().length }}</ng-oat-badge>
          </div>
          <button class="btn ghost small" (click)="clearAll()">Clear all</button>
        </header>
        <div class="vstack gap-1">
          @for (item of notifications(); track item.id) {
            <div class="hstack gap-3 items-start p-2">
              <span class="text-3">{{ item.icon }}</span>
              <div class="vstack gap-1 flex-1">
                <strong class="text-small">{{ item.title }}</strong>
                <span class="text-small text-light">{{ item.description }}</span>
                <small class="text-light">{{ item.time }}</small>
              </div>
              <button class="btn ghost small" (click)="dismiss(item.id)">✕</button>
            </div>
            <hr />
          } @empty {
            <p class="text-center text-light py-4">No notifications</p>
          }
        </div>
      </div>
    </div>
  `,
})
export class NotificationsComponent {
  notifications = signal([
    { id: 1, icon: '🎉', title: 'Welcome!', description: 'Account created.', time: '2 min ago' },
    { id: 2, icon: '💬', title: 'New comment', description: 'Alice replied.', time: '15 min ago' },
    { id: 3, icon: '⭐', title: 'New follower', description: 'Bob followed you.', time: '1 hour ago' },
  ]);

  dismiss(id: number) {
    this.notifications.update(list => list.filter(n => n.id !== id));
  }

  clearAll() {
    this.notifications.set([]);
  }
}