Form controls
Form Field
A wrapper for the label, help and errors around a custom control you place inside.
Import
import { NeuFormFieldComponent } from '@neural-ui/native/form-field';Examples
Import ExampleComponent into its parent and place <app-example /> inside your application’s root neu-theme. The preview supplies the device frame and safe areas; use the root setup from Installation in your app.
Invite your next teammate
Enter an invitation email and validate it. The wrapper surrounds a real projected TextInput with one label, hint and error; it does not create a second input.
View and copy the complete example
import { Component, signal, computed } from '@angular/core';
import { NeuButtonComponent } from '@neural-ui/native/button';
import { NeuFormFieldComponent } from '@neural-ui/native/form-field';
import { View, Text, TextInput, ScrollView } from '@ng-native/components';
@Component({
selector: 'app-example',
imports: [NeuButtonComponent, NeuFormFieldComponent, View, Text, TextInput, ScrollView],
template: `
<scroll-view class="product-scroll" keyboardShouldPersistTaps="handled" [showsVerticalScrollIndicator]="true"><view class="product-stack"><view class="product-section"><text class="product-title" accessibilityRole="header">Invite your next teammate</text><text class="product-caption">A clear label, helpful guidance and recoverable errors</text></view><view class="product-surface"><neu-form-field #emailField label="Email address" hint="We will use this address for invitations" [required]="true" [invalid]="invalid()" error="Enter a valid email address.">
<text-input class="projected-control" [class.invalid]="invalid()"
accessibilityLabel="Email address (required)"
[accessibilityLabelledBy]="emailField.labelId"
[accessibilityHint]="invalid() ? 'Enter a valid email address.' : 'We will use this address for invitations'"
keyboardType="email-address" autoCapitalize="none" [autoCorrect]="false"
[invalid]="invalid()" [touched]="attempted()" [(value)]="email" />
</neu-form-field>
<view class="field-actions">
<neu-button label="Check email" size="sm" (pressed)="attempted.set(true)" />
</view></view></view></scroll-view>
`,
styles: `
.actions { gap: 12px; align-items: flex-start; } text { color: var(--neu-text, #172033); font-size: 16px; }
:host { flex: 1; flex-shrink: 1; min-height: 0; }
.product-scroll { flex: 1; flex-shrink: 1; height: 0px; min-height: 0; }
.product-stack { gap: 24px; padding-bottom: 20px; }
.product-section { gap: 12px; }
.product-row { flex-direction: row; align-items: center; gap: 12px; }
.product-copy { flex: 1; min-width: 0; gap: 4px; }
.product-title { color: var(--neu-text, #172033); font-size: 24px; line-height: 30px; font-weight: 600; }
.product-heading { color: var(--neu-text, #172033); font-size: 17px; line-height: 23px; font-weight: 600; }
.product-caption { color: var(--neu-muted, #596477); font-size: 14px; line-height: 20px; }
.product-actions { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 8px; }
.product-surface { gap: 16px; padding: 16px; border-radius: 16px; background-color: var(--neu-surface, #ffffff); border-width: 1px; border-color: var(--neu-border, #a5afbf); }
.projected-control {
min-height: 48px;
padding: 12px 14px;
font-size: var(--neu-font-size, 16px);
color: var(--neu-text, #172033);
background-color: var(--neu-surface, #ffffff);
border-width: 1px;
border-color: var(--neu-border, #a5afbf);
border-radius: var(--neu-radius, 12px);
}
.projected-control.invalid {
border-color: var(--neu-danger, #b3261e);
}
.field-actions { padding-top: 16px; align-items: flex-start; }
`,
})
export class ExampleComponent {
readonly email = signal('alex@example.com');
readonly attempted = signal(false);
readonly invalid = computed(() => this.attempted() && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email().trim()));
}Change the settings and try the result.
Device captures shown in English.


Personalization
Projected content
Between the field label and its hint or error. Project the actual input control and give it the same accessible label; FormField supplies layout, not a form value.
Use native elements from @ng-native/components and public Neural Native controls. Angular styles belong to the component that declares this content.
API reference
Inputs
| Name | Type | Default | Binding | Description |
|---|---|---|---|---|
| label | string | Required | Input | Visible label above the projected control. Set the inner control’s accessibilityLabel explicitly; the wrapper does not create or label a control automatically. |
| required | boolean | false | Input | Adds an asterisk to the visible label. It does not validate, block editing or require a value in the inner control. |
| invalid | boolean | false | Input | Shows error instead of hint when an error message is provided. Bind the validation state to the inner control separately to style its border. |
| error | string | '' | Input | Application-provided error message, displayed only when invalid is true and this string is nonempty. FormField does not calculate validation rules. |
| hint | string | '' | Input | Help text below the projected content, shown when no active error message replaces it. |
Models
This component declares no two-way models.
Outputs
This component declares no public outputs.