AgentStack
SKILL verified MIT Self-run

Angular Forms

skill-cuongtl1992-vibe-skills-angular-forms · by cuongtl1992

Angular reactive forms with typed FormGroup/FormControl, signal-based validation, dialog form integration, and exhaustMap submission. Use when creating forms, dialog forms, validation, or form submission. ALWAYS use when implementing dialog form patterns, reactive form patterns, or form submission with exhaustMap.

No reviews yet
0 installs
12 views
0.0% view→install

Install

$ agentstack add skill-cuongtl1992-vibe-skills-angular-forms

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Angular Forms? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Angular Reactive Forms

Typed reactive forms with signal-based status tracking, commonly used inside dialog components.

Core Form Setup

import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { toSignal } from '@angular/core/rxjs-interop';
import { startWith } from 'rxjs';

readonly form = new FormGroup({
  name: new FormControl('', {
    nonNullable: true,
    validators: [Validators.required, Validators.maxLength(255)],
  }),
  description: new FormControl('', { nonNullable: true }),
  isActive: new FormControl(true, { nonNullable: true }),
});

Signal-Based Form Status

// MUST use startWith to capture initial status
private readonly formStatus = toSignal(
  this.form.statusChanges.pipe(startWith(this.form.status)),
  { initialValue: this.form.status }
);

readonly saveDisabled = computed(() =>
  this.formStatus() !== 'VALID' || this._submitting()
);

Computed Error Messages

readonly nameErrorMessage = computed(() => {
  const control = this.form.controls.name;
  if (!control.touched) return undefined;
  if (control.hasError('required')) return 'Name is required';
  if (control.hasError('maxlength')) return 'Name must be 255 characters or less';
  return undefined;
});

Submission with exhaustMap

Prevent double-submit using Subject + exhaustMap:

private readonly submitTrigger$ = new Subject();
private readonly destroyRef = inject(DestroyRef);

ngOnInit(): void {
  this.submitTrigger$
    .pipe(
      exhaustMap(() => this.handleSubmission()),
      takeUntilDestroyed(this.destroyRef)  // MUST pass destroyRef
    )
    .subscribe();
}

onSubmit(): void {
  if (this.form.invalid) { this.markFormAsTouched(); return; }
  if (this._submitting()) return;
  this.submitTrigger$.next();
}

private markFormAsTouched(): void {
  Object.values(this.form.controls).forEach(c => c.markAsTouched());
}

Dual Mode (Create/Edit)

mode = input.required();
entity = input();

readonly isCreateMode = computed(() => this.mode() === 'create');

ngOnInit(): void {
  // ... wire submitTrigger$ ...

  if (this.mode() === 'edit') {
    const existing = this.entity();
    if (existing) {
      this.form.patchValue({ name: existing.name, description: existing.description });
    }
  }
}

Key Rules

  • toSignal(form.statusChanges.pipe(startWith(form.status))) — always use startWith
  • Pass this.destroyRef to takeUntilDestroyed() in ngOnInit()
  • Use exhaustMap for submission (prevents concurrent submits)
  • Check touched before showing errors

For full dialog form patterns, see [references/form-patterns.md](references/form-patterns.md).

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.