Read the component API to choose a binding. The same Change suffix does not always mean a model. This table describes the six responsibilities with real Neural examples.
Contract
Who owns state
Can the component apply a new value?
Application receives
Neural example
A ยท INPUT
Application
No: it reads the supplied value.
Nothing automatically.
Button.disabled
B ยท MODEL
Shared editable value
Yes, through the documented model.
The updated value.
Input.value / valueChange
C ยท PROPOSAL
Application
No: application accepts or rejects.
A request, not an applied result.
Dialog.open / closeRequested
D ยท DOMAIN EVENT
Application owns the consequence.
No authoritative state update implied.
Interaction and payload.
Button.neuClick: MouseEvent
E ยท LIFECYCLE
The observed lifecycle
Not an editable value.
Notification after the documented transition.
Dialog.opened / closed
F ยท INTERNAL DERIVED
Component internals
Derives behavior from public state.
No extra public binding.
Button blocks activation when disabled or loading.
The application supplies Button.disabled. Toggling the application signal changes whether the button can activate; Button does not write a new disabled value back. This example is synchronous and only teaches input ownership.
example.ts
import { Component, signal } from '@angular/core';
import { NeuButtonComponent } from '@neural-ui/core/button';
import { NeuSwitchComponent } from '@neural-ui/core/switch';
@Component({
selector: 'app-input-example',
imports: [NeuButtonComponent, NeuSwitchComponent],
template: `
<neu-switch
label="Block action"
[(checked)]="blocked"
/>
<button
neu-button
type="button"
[disabled]="blocked()"
(neuClick)="count.update(increment)"
>
Run
</button>
<output aria-live="polite">{{ count() }}</output>
`,
})
export class InputExample {
readonly blocked = signal(false);
readonly count = signal(0);
readonly increment = (value: number) => value + 1;
}
Input emits valueChange after an edit. The handler updates the same signal that feeds value. Reset updates that signal from the application; there is no second local value. [(value)]="name" is the equivalent two-way form: choose one binding style for a control.
Open Dialog and press Escape. With acceptance enabled, the handler sets open to false. With acceptance disabled, closeRequested still fires but open remains true. Use Finish and close for an explicit application action. Core restores focus when the dialog actually closes.
Do not use [(open)] here: Dialog exposes an input and a request, not an open model.
Button.neuClick reports activation with a MouseEvent. The application decides the consequence. In the first-component example, that consequence is incrementing a counter; it could instead open a task or submit application work.
opened and closed in the Dialog example observe applied open-state transitions. They do not request a change and must not become another open binding. The counters start at zero: Core suppresses an initial lifecycle emission during the first render.
Button derives its blocked activation from disabled and loading. Configure those public inputs; do not bind internal helpers or manually reproduce its disabled semantics.