Appearance
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.