Привязка событий и двусторонняя привязка
События — это способ интерфейса говорить с вашим кодом: клик, ввод, наведение превращаются в вызовы методов компонента.
«Property binding — это монолог компонента. Event binding — диалог с пользователем».
Привязка свойства гонит данные в DOM. Чтобы услышать пользователя, нужна привязка события — круглые скобки. Любое DOM-событие (click, input, submit) можно поймать и вызвать метод класса:
@Component({
selector: 'app-counter',
standalone: true,
template: `
<p>Счёт: {{ count }}</p>
<button (click)="increment()">+1</button>
<input (input)="onType($event)" placeholder="Введите имя" />
<p>Вы ввели: {{ typed }}</p>
`,
})
export class CounterComponent {
count = 0;
typed = '';
increment() { this.count++; }
onType(event: Event) {
this.typed = (event.target as HTMLInputElement).value;
}
}
Переменная $event — это родное DOM-событие. Часто нужно не само событие, а связать поле ввода с переменной в обе стороны: изменилось поле — обновилась переменная, и наоборот. Это двусторонняя привязка, синтаксис «банан в коробке» [()]:
import { Component, model } from '@angular/core';
@Component({
selector: 'app-search',
standalone: true,
template: `
<input [(value)]="query" />
<p>Поиск: {{ query() }}</p>
`,
})
export class SearchComponent {
query = model(''); // двусторонний сигнал
}
Запись [(x)] — это сахар: она разворачивается в [x] (свойство вниз) плюс (xChange) (событие вверх). model() создаёт сигнал, который умеет и читаться, и записываться извне.
Как работает под капотом
«Банан в коробке» [(ngModel)] или [(value)] — не магия, а два связанных биндинга. Angular подставляет привязку свойства и привязку события с суффиксом Change.
[(query)] разворачивается в:
|
+---> [query]="query()" (значение -> input)
+---> (queryChange)="query.set($event)" (input -> значение)
так замыкается двусторонний цикл
Запускаемая врезка: двусторонняя привязка на JS
Соберём мини-«банан в коробке» вручную. «Попробуй сам ▶».
// модель с двусторонней связью: get/set + подписка
function createModel(initial) {
let value = initial;
const subs = [];
return {
get: () => value,
set: (v) => { value = v; subs.forEach(fn => fn(v)); },
onChange: (fn) => subs.push(fn),
};
}
const log = [];
const query = createModel('');
query.onChange(v => log.push('view показывает: ' + v));
query.set('ан'); // пользователь печатает -> view обновился
query.set('анг');
console.log('текущее:', query.get()); // текущее: анг
console.log(log);
// ["view показывает: ан", "view показывает: анг"]
Частые ошибки
- Скобки наоборот.
([x])вместо[(x)]— не сработает; коробка снаружи, банан внутри. - Тяжёлая логика в обработчике клика. Лучше вызвать метод, а не писать выражение прямо в шаблоне.
$event.targetбез приведения типа. TypeScript не знает, что это input; нужноas HTMLInputElement.
Best practices
- Выносите логику обработчиков в методы класса — шаблон остаётся чистым.
- Для форм с
[(ngModel)]не забудьте импортироватьFormsModule. - Используйте
model()для собственных двусторонних компонентов — это современный путь.
Итоги. (событие) ловит действия пользователя, $event даёт данные события, [(x)] связывает значение в обе стороны. Дальше — управление видимостью и списками через новый control flow.
Закрепляем
Если property binding — это монолог компонента в сторону DOM, то event binding превращает связь в диалог. Круглые скобки (событие) подключают обработчик к любому DOM-событию, а двусторонняя привязка [(x)] склеивает оба направления в один удобный синтаксис. Помните мнемонику «банан в коробке»: квадратные скобки снаружи (коробка), круглые внутри (банан) — [()]. Перепутанные скобки ([]) не сработают, и это классическая ошибка новичка.
Под капотом двусторонней привязки нет никакой магии — это просто два обычных биндинга, объединённых конвенцией имён. Когда вы пишете [(value)], Angular ищет вход value и выход valueChange. Именно поэтому model() создаёт сигнал, готовый к двусторонней работе: он автоматически предоставляет и чтение, и парный выход с суффиксом Change. Понимание этого разворачивания снимает ощущение волшебства и помогает строить собственные двусторонние компоненты осознанно.
| Синтаксис | Направление |
|---|---|
| (click)="fn()" | Событие DOM в код |
| $event | Объект события (target.value) |
| [(value)]="x" | Двусторонняя привязка |
| model() | Сигнал для [(...)] |