Skip to content

Angular

Every example on this page is a self-contained standalone Angular component using fetch - no BootForm SDK to install. Jump to just the one you need from the outline on the right, or read top to bottom for the full picture.

Basic submission

The plain HTML case works unchanged inside a template - no component logic needed at all:

html
<!-- contact-form.component.html -->
<form action="https://f.bootform.com/{form_id}" method="POST">
  <input name="email" type="email" required />
  <textarea name="message"></textarea>
  <button type="submit">Send</button>
</form>

AJAX submission

Intercept the submit, post as JSON, and track status with a component field instead of navigating away:

ts
// contact-form.component.ts
import { Component } from '@angular/core'

type Status = 'idle' | 'sending' | 'sent' | 'error'

@Component({
  selector: 'app-contact-form',
  standalone: true,
  templateUrl: './contact-form.component.html',
})
export class ContactFormComponent {
  status: Status = 'idle'
  error: string | null = null

  async handleSubmit(event: SubmitEvent) {
    event.preventDefault()
    const form = event.target as HTMLFormElement
    this.status = 'sending'

    const response = await fetch(form.action, {
      method: 'POST',
      headers: { Accept: 'application/json' },
      body: new FormData(form),
    })
    const result = await response.json()

    if (result.ok) {
      form.reset()
      this.status = 'sent'
    } else {
      this.error = result.error
      this.status = 'error'
    }
  }
}
html
<!-- contact-form.component.html -->
<form action="https://f.bootform.com/{form_id}" method="POST" (submit)="handleSubmit($event)">
  <input name="email" type="email" required />
  <textarea name="message"></textarea>
  <button type="submit" [disabled]="status === 'sending'">
    {{ status === 'sending' ? 'Sending…' : 'Send' }}
  </button>
  <p *ngIf="status === 'sent'">Thanks - your message was sent.</p>
  <p *ngIf="status === 'error'" role="alert">{{ error }}</p>
</form>

File upload

FormData picks up a file input the same way as text fields - nothing Angular-specific needed beyond the status field you're already tracking above:

ts
// upload-form.component.ts
async handleSubmit(event: SubmitEvent) {
  event.preventDefault()
  const form = event.target as HTMLFormElement
  this.status = 'sending'

  const response = await fetch(form.action, {
    method: 'POST',
    headers: { Accept: 'application/json' },
    body: new FormData(form), // includes the selected file automatically
  })
  const result = await response.json()
  this.status = result.ok ? 'sent' : 'error'
}
html
<!-- upload-form.component.html -->
<form action="https://f.bootform.com/{form_id}" method="POST" (submit)="handleSubmit($event)">
  <input name="email" type="email" required />
  <input name="resume" type="file" required />
  <button type="submit" [disabled]="status === 'sending'">Send</button>
</form>

A file upload requires the form to be claimed first - 422 FORM_NOT_CLAIMED otherwise. See Building your form.

Moderated content feed

Load a form's approved submissions on init, and let visitors react - the feed endpoint is public and unauthenticated, no API key involved:

ts
// content-feed.component.ts
import { Component, Input, OnInit } from '@angular/core'

interface FeedItem {
  id: string
  fields: Record<string, string>
  reactions: { up: number; down: number }
}

@Component({
  selector: 'app-content-feed',
  standalone: true,
  templateUrl: './content-feed.component.html',
})
export class ContentFeedComponent implements OnInit {
  @Input() formId!: string
  items: FeedItem[] = []

  async loadFeed() {
    const response = await fetch(`https://f.bootform.com/forms/${this.formId}/feed?per_page=20`)
    const { items } = await response.json()
    this.items = items
  }

  async react(submissionId: string, type: 'up' | 'down') {
    await fetch(`https://f.bootform.com/forms/${this.formId}/feed/${submissionId}/react`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ type }),
    })
    this.loadFeed() // refetch to show the updated count
  }

  ngOnInit() {
    this.loadFeed()
  }
}
html
<!-- content-feed.component.html -->
<ul>
  <li *ngFor="let item of items">
    <p>{{ item.fields['message'] }}</p>
    <button (click)="react(item.id, 'up')">👍 {{ item.reactions.up }}</button>
    <button (click)="react(item.id, 'down')">👎 {{ item.reactions.down }}</button>
  </li>
</ul>

See Moderated content feed for the report endpoint, pagination, and the hosted-widget alternative if you'd rather not write this yourself.