Содержание

Модуль FormsModule и директива NgModel

Для взаимодействия с пользователем в веб-приложениях, как правило применяются формы. В Angular прежде чем использовать формы в компонентах, нам надо импортировать в главном модуле AppModule модуль FormsModule, который позволяет работать с формами:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent }   from './app.component';
 
import { FormsModule }   from '@angular/forms';
 
@NgModule({
    imports:      [ BrowserModule, FormsModule],
    declarations: [ AppComponent],
    bootstrap:    [ AppComponent ]
})
export class AppModule { }

Кроме того, в файле конфигурации приложения package.json среди списка используемых зависимостей должен быть указан пакет «angular/forms»:

{
    "name": "helloapp",
    "version": "1.0.0",
    "description": "First Angular 11 Project",
    "author": "Eugene Popov metanit.com",
    "scripts": {
        // команды angular cli
    },
    "dependencies": {
        "@angular/forms": "~11.0.0",
        // остальные пакеты
    },
    "devDependencies": {
 
        // остальные пакеты
    }
}

При работе с формами ключевым моментом является использование директивы NgModel. Эта директива с помощью переданной модели создает объект FormControl и привязывает эту модель к созданному элементу формы. Объект FormControl отслеживает значение модели, а также отвечает за валидацию этого значения и взаимодействие с пользователем.

Данная директива принимает переданную ей модель в качестве входного свойства. Причем мы можем использовать как однонаправленную, так и двунаправленную привязку.

Если нам надо просто вывести значение модели в поле ввода, то можно ограничиться и однонаправленной привязкой:

<input name="title" [ngModel]="title" />

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

Если нам надо отслеживать изменение введенных данных, то мы можем использовать двунаправленную привязку:

<input name="title" [(ngModel)]="title" />

Однонаправленная привязка пример

import {Component, ViewChild} from '@angular/core';
import {NgForm} from "@angular/forms";
 
 
@Component({
  selector: 'app-root',
  template: `
    <div class="col-xs-8 col-xs-offset-2">
      <form (ngSubmit)="submitForm(myForm)" #myForm="ngForm">
 
        <div class="form-group">
          <label>Email</label>
          <input ngModel name="email" type="text" class="form-control">
        </div>
        <div class="form-group">
          <label>Пароль</label>
          <input ngModel name="pass" type="password" class="form-control">
        </div>
        <div class="form-group">
          <label>Выберите страну</label>
          <select ngModel name="country" class="form-control">
            <option value="ru">Россия</option>
            <option value="by">Белоруссия</option>
            <option value="ua">Украина</option>
          </select>
        </div>
        <div class="radio" *ngFor="let ans of answers">
          <label>
            <input ngModel name="answer" type="radio" [value]="ans.type"> {{ans.text}}
          </label>
        </div>
        <button class="btn btn-success" type="submit">Сохранить</button>
 
      </form>
    </div>
  `
})
export class AppComponent {
  answers = [{
    type: 'yes',
    text: 'Да'
  }, {
    type: 'no',
    text: 'Нет'
  }];
 
  submitForm(form: NgForm){
 
    console.log('submit', form);
  }
}

Однонаправленная привязка с ViewChild пример

import {Component, ViewChild} from '@angular/core';
 
@Component({
  selector: 'app-root',
  template: `
    <div class="col-xs-8 col-xs-offset-2">
      <form (ngSubmit)="submitForm()" #myForm="ngForm">
 
        <div class="form-group">
          <label>Email</label>
          <input ngModel name="email" type="text" class="form-control">
        </div>
        <div class="form-group">
          <label>Пароль</label>
          <input ngModel name="pass" type="password" class="form-control">
        </div>
        <div class="form-group">
          <label>Выберите страну</label>
          <select ngModel name="country" class="form-control">
            <option value="ru">Россия</option>
            <option value="by">Белоруссия</option>
            <option value="ua">Украина</option>
          </select>
        </div>
        <div class="radio" *ngFor="let ans of answers">
          <label>
            <input ngModel name="answer" type="radio" [value]="ans.type"> {{ans.text}}
          </label>
        </div>
        <button class="btn btn-success" type="submit">Сохранить</button>
 
      </form>
    </div>
  `
})
export class AppComponent {
  answers = [{
    type: 'yes',
    text: 'Да'
  }, {
    type: 'no',
    text: 'Нет'
  }];
  @ViewChild('myForm') form: any
  submitForm(){
 
    console.log('submit', this.form);
  }
}




Двунаправленная привязка пример

Рассмотрим применение NgModel на примере. Возьмем проект с базовой структурой:



Определим в файле app.component.ts следующий компонент:

import { Component} from '@angular/core';
 
export class Phone{
    constructor(public title: string, 
                public price: number, 
                public company: string)
    { }
}
 
@Component({
    selector: 'my-app',
    template: `<div> 
                    <div class="form-group">
                        <label>Название модели</label>
                        <input class="form-control" name="title" [(ngModel)]="title" />
                    </div>
                    <div class="form-group">
                        <label>Цена</label>
                        <input type="number" class="form-control" name="price" [(ngModel)]="price" />
                    </div>
                    <div class="form-group">
                        <label>Производитель</label>
                        <select class="form-control" name="company" [(ngModel)]="company">
                            <option  *ngFor="let comp of companies" [value]="comp">
                                {{comp}}
                            </option>
                        </select>
                    </div>
                    <div class="form-group">
                        <button class="btn btn-default" (click)="addPhone()">Добавить</button>
                    </div>
              </div>
              <div><h3>Добавленные элементы</h3>
                    <ul *ngFor="let p of phones">
                        <li>{{p.title}} ({{p.company}}) - {{p.price}}</li>
                    </ul>
              </div>`
})
export class AppComponent { 
 
    title: string;
    price: number;
    company: string;
 
    phones: Phone[] = [];
    companies: string[] = ["Apple", "Huawei", "Xiaomi", "Samsung", "LG", "Motorola", "Alcatel"];
 
    addPhone(){
        this.phones.push(new Phone(this.title, this.price, this.company));
    }
}

Для представления данных здесь определен класс Phone, в котором есть три свойства. Класс компонента содержит массив объектов Phone. С помощью метода addPhone() в этот массив добавляется новый объект.

Для добавления данных в шаблоне определены три поля ввода. В каждом поле определены директивы типа [(ngModel)]=«title». Тем самым фактически определяются некоторые значения, которые привязаны к этим полям. В обработчике нажатия кнопки вызывается метод addPhone(), в который передаются эти значения.

В конце шаблона добавленные данные из массива phones выводятся на страницу:


Все три поля привязаны к отдельным значениям, которые существуют сами по себе. Но мы можем пойти дальше и определить для формы ввода отдельную модель, которая будет инкапсулировать эти значения:

import { Component} from '@angular/core';
 
export class Phone{
    constructor(public title: string, 
                public price: number, 
                public company: string)
    { }
}
 
@Component({
    selector: 'my-app',
    template: `<div> 
                    <div class="form-group">
                        <label>Название модели</label>
                        <input class="form-control" name="title" [(ngModel)]="phone.title" />
                    </div>
                    <div class="form-group">
                        <label>Цена</label>
                        <input type="number" class="form-control" name="price" [(ngModel)]="phone.price" />
                    </div>
                    <div class="form-group">
                        <label>Производитель</label>
                        <select class="form-control" name="company" [(ngModel)]="phone.company">
                            <option  *ngFor="let comp of companies" [value]="comp">
                                {{comp}}
                            </option>
                        </select>
                    </div>
                    <div class="form-group">
                        <button class="btn btn-default" (click)="addPhone()">Добавить</button>
                    </div>
              </div>
              <div><h3>Добавленные элементы</h3>
                    <ul *ngFor="let p of phones">
                        <li>{{p.title}} ({{p.company}}) - {{p.price}}</li>
                    </ul>
              </div>`
})
export class AppComponent { 
 
    phone: Phone = new Phone("", 0, "Huawei");
    phones: Phone[] = [];
    companies: string[] = ["Apple", "Huawei", "Xiaomi", "Samsung", "LG", "Motorola", "Alcatel"];
 
    addPhone(){
        this.phones.push(new Phone(this.phone.title, this.phone.price, this.phone.company));
    }
}

Для полей ввода здесь создана отдельная переменная phone, к свойствам которой привязаны поля ввода. Стоит также обратить внимание на то, как добавляется новый объект в массив phones - здесь не добавляется напрямую переменная phone, а создается отдельный объект, который инициализируется значениями из переменной phone. А в остальном результат будет тем же, что и в предыдущем примере.