Skip to main content
Neural Nativev0.1.0-beta.1Documentation

Import

component.ts
import { NeuRadioGroupComponent } from '@neural-ui/native/radio';

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.

A plan that fits your pace

Choose a single billing period and compare the illustrative total, payment frequency and annual saving. The inclusions stay the same in both periods. Selecting a radio changes the preview without making a purchase.

View and copy the complete example
basic.component.ts
import { Component, signal } from '@angular/core';
import { NeuBadgeComponent } from '@neural-ui/native/badge';
import { NeuDividerComponent } from '@neural-ui/native/divider';
import { NeuRadioGroupComponent } from '@neural-ui/native/radio';
import { View, Text, ScrollView } from '@ng-native/components';
import type { NeuOption } from '@neural-ui/native';

@Component({
  selector: 'app-example',
  imports: [NeuBadgeComponent, NeuDividerComponent, NeuRadioGroupComponent, View, Text, ScrollView],
  template: `
    <scroll-view class="product-scroll" keyboardShouldPersistTaps="handled" [showsVerticalScrollIndicator]="false"><view class="product-stack"><view class="product-section"><text class="product-title" accessibilityRole="header">A plan that fits your pace</text><text class="product-caption">Your ideas deserve a little room</text></view>
    <view class="plan-summary"><view class="product-section"><text class="plan-kicker">NEURAL STUDIO</text><text class="plan-title">Room to create</text><text class="plan-description">A shared home for your next big idea.</text></view>
    <view class="plan-price-row"><text class="plan-price">{{ plan() === 'annual' ? '120 €' : '12 €' }}</text><text class="plan-period">{{ plan() === 'annual' ? '/ year' : '/ month' }}</text></view>
    <view class="plan-detail"><text class="plan-description">{{ plan() === 'annual' ? 'Equivalent to €10 a month. One €120 yearly payment.': 'One €12 payment each month. No annual commitment.' }}</text></view></view>
    <view class="product-surface"><view class="product-row"><view class="product-copy"><text class="product-heading">Choose your rhythm</text></view>@if (plan() === 'annual') { <neu-badge label="Save €24 a year" tone="success" /> }</view><neu-radio label="Billing plan" [options]="plans" [(value)]="plan" />
    <neu-divider /><text class="product-caption">Included with either period</text><view class="plan-features">@for (feature of features; track feature) { <view class="product-row"><view class="plan-feature-mark"><text class="plan-feature-check">✓</text></view><view class="product-copy"><text class="product-caption">{{ feature }}</text></view></view> }</view></view>
    <text class="product-caption">Illustrative plan and prices. Choosing a period makes no purchase.</text></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); }
.plan-summary { gap: 22px; padding: 22px; border-radius: 20px; background-color: #153760; }
.plan-kicker { color: #a8c8ee; font-size: 10px; line-height: 16px; font-weight: 600; letter-spacing: 1px; }
.plan-title { color: #f8fbff; font-size: 26px; line-height: 32px; font-weight: 600; }
.plan-description { color: #c1d4ec; font-size: 13px; line-height: 19px; }
.plan-price-row { flex-direction: row; align-items: flex-end; gap: 9px; }
.plan-price { color: #f8fbff; font-size: 40px; line-height: 46px; font-weight: 600; }
.plan-period { color: #c1d4ec; font-size: 14px; line-height: 22px; padding-bottom: 4px; }
.plan-detail { border-top-width: 1px; border-color: #36567d; padding-top: 14px; }
.plan-features { gap: 12px; }
.plan-feature-mark { width: 20px; height: 20px; border-radius: 10px; align-items: center; justify-content: center; background-color: #e2eee8; }
.plan-feature-check { color: #2d6751; font-size: 12px; line-height: 16px; font-weight: 600; }
  `,
})
export class ExampleComponent {
  readonly plans: readonly NeuOption[] = [
    {
      "id": "monthly",
      "label": "Monthly",
      "description": "Flexible billing"
    },
    {
      "id": "annual",
      "label": "Annual",
      "description": "One payment per year"
    }
  ];
  readonly plan = signal('monthly');
  readonly features = ['Unlimited project drafts', 'One shared team workspace', 'Your files and ideas, together'];
}

Validation and unavailable choices

Choose the available delivery method to clear the required error.

View and copy the complete example
validation-and-unavailable-choices.component.ts
import { Component, signal } from '@angular/core';
import { NeuRadioGroupComponent } from '@neural-ui/native/radio';
import { View, ScrollView } from '@ng-native/components';
import type { NeuOption } from '@neural-ui/native';

@Component({
  selector: 'app-example',
  imports: [NeuRadioGroupComponent, View, ScrollView],
  template: `
    <scroll-view class="usage-scroll" keyboardShouldPersistTaps="handled"><view class="usage-stack">
    <neu-radio label="Required delivery method" [options]="delivery" [(value)]="deliveryMethod" [invalid]="!deliveryMethod()" [touched]="true" error="Choose a delivery method." />
    </view></scroll-view>
  `,
  styles: `
.actions { gap: 12px; align-items: flex-start; } text { color: var(--neu-text, #172033); font-size: 16px; }

.usage-scroll { flex: 1; }
.usage-stack { gap: 20px; padding-bottom: 20px; }
.usage-section { gap: 12px; }
.usage-heading { color: var(--neu-text, #172033); font-size: 16px; font-weight: 600; }
.usage-caption { color: var(--neu-muted, #64748b); font-size: 14px; }
.usage-row { flex-direction: row; flex-wrap: wrap; gap: 12px; align-items: center; }
.usage-actions { flex-direction: row; flex-wrap: wrap; gap: 8px; align-items: center; }


  `,
})
export class ExampleComponent {
  readonly delivery = [{ id: 'standard', label: 'Standard', description: 'Three working days' }, { id: 'express', label: 'Express', description: 'Unavailable for this address', disabled: true }];
  readonly deliveryMethod = signal('');
}
Interactive playground

Change the settings and try the result.

Device captures shown in English.

Radio · android · native capture
AndroidNative screenshot
Radio · ios · native capture
iOSNative screenshot

API reference

Inputs

NameTypeDefaultBindingDescription
labelstringRequiredInputHuman-readable name for the control, action or group.
optionsreadonly NeuOption[][]InputAvailable choices. Selected values refer to option IDs, not their displayed labels.
disabledbooleanfalseInputDisables user interaction. Values can still be supplied by the application.
readonlybooleanfalseInputPrevents user editing or selection while retaining the current value.
invalidbooleanfalseInputUses the theme danger color for all option rings and the selected dot. Displays error when provided. The application owns validation; invalid does not change the selected value or prevent choosing an option.
touchedbooleanfalseInputField interaction state, supplied manually or through Signal Forms.
errorstring''InputApplication-provided error message; combine it with the control’s invalid state.

Models

NameTypeDefaultBindingDescription
valuestring''Model · two-wayCurrent edited value or selected ID(s), using the declared type. Bind a writable signal with [(value)].

Outputs

NamePayloadWhen it emits
valueChangestringEmits the updated value value. [(value)] binds this output together with its model input.
touchvoidReports user interaction to the surrounding form or application.

Model state

Bind the signal with [(modelName)] without calling it. Use modelName() to read its value.

model-fields.ts
import { signal } from '@angular/core';

// Fields inside your component class
readonly value = signal<string>('');

Types and data interfaces

Use the imports in your application. The definitions below are provided for reference.

NeuOption

NeuOption.ts
import type { NeuOption } from '@neural-ui/native';

// Public definition for reference only. Import this type; do not redeclare it.
export interface NeuOption {
    readonly id: string;
    readonly label: string;
    readonly description?: string;
    readonly disabled?: boolean;
}
FieldTypeRequirementMeaning
idstringRequiredStable, unique identifier used for tracking and selection.
labelstringRequiredHuman-readable item name.
descriptionstringOptionalOptional supporting text; rendering depends on the control.
disabledbooleanOptionalPrevents activating or selecting this item.