import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
export interface ConfirmDialogData {
title: string;
message: string;
confirmText: string;
cancelText: string;
}
@Component({
selector: 'app-confirm-dialog',
template: `
<h2 mat-dialog-title>{{ data.title }}</h2>
<mat-dialog-content>{{ data.message }}</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-button (click)="cancel()">{{ data.cancelText }}</button>
<button mat-raised-button color="warn" (click)="confirm()">
{{ data.confirmText }}
</button>
</mat-dialog-actions>
`,
})
export class ConfirmDialogComponent {
constructor(
private readonly dialogRef: MatDialogRef<ConfirmDialogComponent, boolean>,
@Inject(MAT_DIALOG_DATA) public readonly data: ConfirmDialogData
) {}
confirm(): void {
this.dialogRef.close(true);
}
cancel(): void {
this.dialogRef.close(false);
}
}
import { Injectable } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ConfirmDialogComponent, ConfirmDialogData } from './confirm-dialog.component';
const DEFAULTS: ConfirmDialogData = {
title: 'Please confirm',
message: 'Are you sure?',
confirmText: 'Confirm',
cancelText: 'Cancel',
};
@Injectable({ providedIn: 'root' })
export class ConfirmationService {
constructor(private readonly dialog: MatDialog) {}
confirm(options: Partial<ConfirmDialogData>): Observable<boolean> {
const data: ConfirmDialogData = { ...DEFAULTS, ...options };
const ref = this.dialog.open<
ConfirmDialogComponent,
ConfirmDialogData,
boolean
>(ConfirmDialogComponent, {
data,
width: '420px',
disableClose: false,
autoFocus: false,
});
// undefined (backdrop / escape) is normalized to a strict false
return ref.afterClosed().pipe(map((result) => result === true));
}
}
import { Component } from '@angular/core';
import { EMPTY } from 'rxjs';
import { filter, switchMap, catchError } from 'rxjs/operators';
import { ConfirmationService } from './confirmation.service';
import { AccountService } from './account.service';
@Component({
selector: 'app-danger-zone',
template: `
<button mat-stroked-button color="warn" (click)="deleteAccount()">
Delete account
</button>
`,
})
export class DangerZoneComponent {
constructor(
private readonly confirmation: ConfirmationService,
private readonly accounts: AccountService
) {}
deleteAccount(): void {
this.confirmation
.confirm({
title: 'Delete account',
message: 'This permanently removes all your data. Continue?',
confirmText: 'Delete',
})
.pipe(
filter((confirmed) => confirmed),
switchMap(() => this.accounts.deleteCurrent()),
catchError(() => EMPTY)
)
.subscribe(() => this.accounts.signOut());
}
}
This snippet shows a small, self-contained confirmation dialog built the idiomatic Angular way: a stateless dialog component driven by injected data, plus a thin service that opens it and hands back an Observable<boolean> representing the user's choice. The pattern solves a common friction point — calling code that wants to ask "are you sure?" without importing Angular Material APIs, wiring up dialog refs, or managing subscriptions everywhere.
In ConfirmDialogComponent, the component knows nothing about who opened it. It reads its title, message, and button labels from MAT_DIALOG_DATA via a typed ConfirmDialogData interface, and closes itself with a boolean payload through MatDialogRef.close. Keeping the component presentational means it is trivially reusable and testable; the confirm() and cancel() methods simply resolve the dialog to true or false.
In ConfirmationService, MatDialog.open returns a MatDialogRef, and afterClosed() already exposes an Observable. The service normalizes that stream: map(result => result === true) collapses the boolean | undefined case (the user pressing Escape or clicking the backdrop yields undefined) into a strict boolean, so consumers never have to guard against undefined. Sensible defaults are merged into the data object so callers can pass only a message. Returning a cold-ish, single-emission Observable fits Angular's reactive style and composes cleanly with operators like filter and switchMap.
In DangerZoneComponent, the payoff is visible: deleteAccount() calls confirmation.confirm(...), then filter(confirmed => confirmed) short-circuits when the user declines, and switchMap chains straight into the actual delete request. Because the confirmation is just another Observable in the pipeline, cancellation and the real work read as one linear flow rather than nested callbacks.
The main trade-off is that afterClosed() emits once and completes, so it behaves like a promise-shaped stream; for repeated prompts the service must be called again. A subtle pitfall is treating undefined as anything other than a decline — the explicit === true check guards against that. This approach is worth reaching for whenever confirmation logic is duplicated across components and should live behind one injectable seam.
Related snips
export type Settled<R> =
| { status: 'fulfilled'; value: R }
| { status: 'rejected'; reason: unknown };
export interface ConcurrencyOptions {
limit: number;
Simple concurrency limiter for batch operations
<h1>Products</h1>
<%= form_with url: products_path, method: :get,
data: { turbo_frame: "products_list", turbo_action: "advance" } do |f| %>
<div class="filters">
<%= f.text_field :q, value: params[:q], placeholder: "Search products" %>
Frame navigation that targets a specific frame via form_with
import SwiftUI
struct CardModifier: ViewModifier {
var backgroundColor: Color = .white
var cornerRadius: CGFloat = 12
var shadowRadius: CGFloat = 5
Custom SwiftUI view modifiers for reusability
/* Interactive pseudo-classes */
a:link { color: blue; }
a:visited { color: purple; }
a:hover { text-decoration: underline; }
a:active { color: red; }
a:focus { outline: 2px solid orange; }
CSS pseudo-classes and pseudo-elements for advanced styling
import { useEffect, useState } from "react";
export function useDebounce<T>(value: T, delay = 300): T {
const [debounced, setDebounced] = useState<T>(value);
useEffect(() => {
Debounced search input (React)
# Using the module
module "api_service" {
source = "./modules/ecs_service"
service_name = "api"
Terraform modules for reusable infrastructure
Share this code
Here's the card — post it anywhere.