JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Использование [(ngModel)]<...

Использование [(ngModel)] для привязки полей формы в Angular

Модуль 4: Node.js, Next.js и Angular
16 уровень , 2 лекция
Открыта

1. Знакомство с [(ngModel)]

Если вы когда-нибудь писали формы на чистом HTML или даже в React, то знаете, как иногда утомительно следить за состоянием каждого поля: нужно писать отдельные обработчики событий, вручную обновлять значения, синхронизировать данные между моделью и представлением. В Angular Template-driven Forms всё становится проще благодаря директиве ngModel.

[(ngModel)] — это директива для двусторонней привязки данных. Она связывает переменную в компоненте и поле формы в шаблоне так, что любые изменения в одном месте мгновенно отражаются в другом. Это как постоянный телепатический канал между вашим компонентом и HTML!

Если бы у вас был такой канал с холодильником, вы бы всегда знали, когда там заканчивается молоко. Но пока Angular не умеет связывать холодильники, он прекрасно работает с формами.


- [(ngModel)] связывает значение поля формы и переменную компонента.
- Любое изменение в input — меняет переменную.
- Любое изменение переменной — меняет input.

Подключение FormsModule

Перед тем как использовать [(ngModel)], убедитесь, что вы подключили модуль FormsModule в вашем приложении. Без этого Angular будет смотреть на ваши скобочки как на странный набор символов.

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; // Вот он, FormsModule!

import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, FormsModule], // Добавьте FormsModule сюда!
  bootstrap: [AppComponent]
})
export class AppModule {}

2. Синтаксис [(ngModel)]: как это выглядит

Синтаксис двусторонней привязки в Angular — это не просто квадратные или круглые скобки, а их комбо:

<input [(ngModel)]="username">

Пояснение:

  • Квадратные скобки [ ] — это привязка значения (property binding).
  • Круглые скобки ( ) — это слушатель событий (event binding).
  • Вместе они дают "banana in a box" — [( )]. (Да, в англоязычном сообществе это реально называют "банан в коробке" из-за внешнего вида.)

Пример в шаблоне:

<input [(ngModel)]="username" placeholder="Введите имя">
<p>Вы ввели: {{ username }}</p>

В компоненте:

export class AppComponent {
  username = '';
}

Каждый раз, когда вы вводите буквы в поле, переменная username автоматически обновляется, и наоборот.

3. Пример: Полная форма с [(ngModel)]

Давайте сделаем простую форму регистрации пользователя с полями "Имя" и "Email". Мы будем использовать [(ngModel)] для каждого поля.

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  name = '';
  email = '';

  submitForm() {
    alert(`Имя: ${this.name}\nEmail: ${this.email}`);
  }
}

app.component.html

<form (ngSubmit)="submitForm()">
  <label>
    Имя:
    <input [(ngModel)]="name" name="name" required>
  </label>
  <br>
  <label>
    Email:
    <input [(ngModel)]="email" name="email" required>
  </label>
  <br>
  <button type="submit">Зарегистрироваться</button>
</form>

<p>Текущее имя: {{ name }}</p>
<p>Текущий email: {{ email }}</p>

Важный момент:
У каждого input, использующего [(ngModel)] внутри формы, должно быть указано свойство name. Без этого Angular будет ворчать и не сможет правильно связать поля с формой.

4. Как работает двусторонняя привязка под капотом?

Когда вы используете [(ngModel)], Angular автоматически:

  • Устанавливает значение input из переменной компонента.
  • Слушает событие ввода пользователя (input), чтобы обновить переменную.
  • Если переменная изменится в компоненте (например, по кнопке), input тоже обновится.

Это работает не только для input, но и для select, textarea и даже radio-кнопок!

Пример с textarea и select

<textarea [(ngModel)]="message" name="message"></textarea>
<select [(ngModel)]="country" name="country">
  <option value="ru">Россия</option>
  <option value="us">США</option>
  <option value="cn">Китай</option>
</select>

5. Полезные нюансы

[(ngModel)] вне формы: можно, но осторожно!

Иногда вам нужно просто связать переменную и input без формы. Это работает, но Angular будет ругаться в консоли, если вы не укажете атрибут name. Для input вне формы можно не указывать name, но если появятся ошибки — просто добавьте его.

<input [(ngModel)]="search" placeholder="Поиск">

Привязка к объектам и массивам

[(ngModel)] можно использовать не только с простыми переменными, но и с полями объектов:

user = {
  firstName: '',
  lastName: ''
};
<input [(ngModel)]="user.firstName" name="firstName">
<input [(ngModel)]="user.lastName" name="lastName">

Работает и с элементами массива (хотя тут уже нужно быть осторожнее):

tags = ['', '', ''];
<input [(ngModel)]="tags[0]" name="tag1">
<input [(ngModel)]="tags[1]" name="tag2">
<input [(ngModel)]="tags[2]" name="tag3">

[(ngModel)] и валидация

Angular автоматически добавляет CSS-классы и свойства для валидации input-ов с [(ngModel)]:

  • ng-valid — значение валидно.
  • ng-invalid — невалидно.
  • ng-touched — поле было в фокусе.
  • ng-untouched — поле не трогали.

Это позволяет легко стилизовать поля в зависимости от их состояния.

Пример:

<input #emailInput="ngModel" [(ngModel)]="email" name="email" required email>
<span *ngIf="emailInput.invalid && emailInput.touched" style="color: red;">
  Некорректный email!
</span>

[(ngModel)] и радиокнопки, чекбоксы

Радиокнопки:

<label>
  <input type="radio" name="gender" [(ngModel)]="gender" value="male"> Мужчина
</label>
<label>
  <input type="radio" name="gender" [(ngModel)]="gender" value="female"> Женщина
</label>

Чекбоксы:

<input type="checkbox" [(ngModel)]="isActive" name="active">
<p>Активен: {{ isActive }}</p>

[(ngModel)] и события: реагируем на изменения

Иногда нужно что-то делать при каждом изменении значения. Можно использовать событие ngModelChange:

<input [(ngModel)]="username" (ngModelChange)="onUsernameChange($event)" name="username">
onUsernameChange(value: string) {
  console.log('Новое имя:', value);
}

6. Практика: мини-приложение «Калькулятор»

Давайте расширим наше учебное приложение и добавим простейший калькулятор, используя [(ngModel)] для двусторонней привязки:

app.component.ts

export class AppComponent {
  num1 = 0;
  num2 = 0;
  result = 0;

  add() {
    this.result = this.num1 + this.num2;
  }
}

app.component.html

<input type="number" [(ngModel)]="num1" name="num1">
+
<input type="number" [(ngModel)]="num2" name="num2">
<button (click)="add()">=</button>
<span>{{ result }}</span>

Теперь любые изменения в input сразу попадают в переменные компонента, и при нажатии на кнопку результат вычисляется.

7. Типичные ошибки при использовании [(ngModel)]

Ошибка №1: забыли FormsModule.
Если вы забыли импортировать FormsModule в свой модуль, Angular выдаст ошибку вида:
Can't bind to 'ngModel' since it isn't a known property of 'input'.
Всегда проверяйте, что FormsModule есть в разделе imports.

Ошибка №2: забыли атрибут name в input внутри формы.
Без атрибута name Angular не сможет правильно связать поле с формой, и вы получите ошибку в консоли.

Ошибка №3: используете [(ngModel)] с реактивными формами.
В Reactive Forms [(ngModel)] не нужен и может привести к конфликтам и предупреждениям. Не смешивайте подходы!

Ошибка №4: двусторонняя привязка к несуществующей переменной.
Если вы написали [(ngModel)]="foo.bar" а объекта foo нет — получите ошибку Cannot read property 'bar' of undefined.
Всегда инициализируйте объекты и массивы заранее.

Ошибка №5: забыли, что [(ngModel)] работает только с простыми типами.
Попытка привязать [(ngModel)] к сложному выражению (например, функции или вычисляемому значению) не сработает.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ