Итак, в предыдущих разделах мы подготовили проект к практической работе. В данном разделе мы рассмотрим, что такое привязка данных («байндинг», от англ. binding — связывание) в Angular и как ее делать.

Привязки используются для передачи каких-либо данных из компонента в шаблон и обратно. Есть несколько типов привязок в Angular:

  • Строчная интерполяция — привязка содержимого html-элемента к значению переменной из компонента. Выглядит следующим образом {{ value }}. Это односторонняя привязка, используется для того, чтобы передать переменную в компонент Angular.
  • Привязка свойств (атрибутов) html-элемента. Например,
    <input type="text" [value]="input.value">
    В данном примере в атрибут value будет занесено значение свойства value объекта input, манипуляции над которым будут производиться в компоненте.
  • Привязка событий — это привязка метода определенного в компоненте к событию DOM, например к клику
    <button (click)="changeData(title, price)">Изменить</button>
    В этом случае при клике на кнопку будет срабатывать метод changeData() с переданными ему параметрами title и price
  • Также существует двусторонняя привязка. Она используется в элементах форм.
    <input [(ngModel)]="title">
    В таком случае изменение значения в компоненте приводит к моментальному изменению значения в шаблоне, и наоборот, если к примеру сделать это к текстовому полю шаблона, то как только будет введено какое-либо значение, оно сразу будет передаваться в компонент.

Строчная интерполяция

Протестируем первый вид привязки. Изменим код шаблона, а именно его заголовок:

Листинг 4.1 | Файл: src/app/all-products/all-products.component.html
<div class="container">
  <div class="row">
    <h3>{{ title }}</h3>
    <table class="table table-striped text-center">
        <thead class="thead-dark">

Затем изменим файл компонента и добавим переменную title

Листинг 4.2 | Файл: src/app/all-products/all-products.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from '../shared/data.service';

@Component({
  selector: 'app-all-products',
  templateUrl: './all-products.component.html',
  styleUrls: ['./all-products.component.css']
})
export class AllProductsComponent implements OnInit {

	title = 'Каталог товаров';

	products = [];

  constructor(private dataService: DataService) { }

  ngOnInit() {
  	this.products = this.dataService.products;
  }
}

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

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

Привязка свойств и событий

Теперь сделаем второй и третий тип привязки. Добавим в разметку следующий элемент:

Листинг 4.3 | Файл: src/app/all-products/all-products.component.html
<div class="container">
	<div class="row">
		<h3 class="mr-3 mt-2">{{ title }}</h3>
		<div class="form-inline">
			<input type="checkbox" class="mr-2" 
				[checked]="isChecked" 
				(click)="changeIsChecked()">
			<input type="text" class="form-control mr-3" 
				[value]="title" 
				[disabled]="isChecked">
			<button class="btn btn-primary" 
				[disabled]="isChecked" 
				(click)="editTitle()">Изменить</button>
		</div> <!-- /form-inline -->
.....

Для большей наглядности привязки перенесены на новую строку.

В файле компонента сделаем следующие изменения:

Листинг 4.4 | Файл: src/app/all-products/all-products.component.ts
...
export class AllProductsComponent implements OnInit {

	title = 'Каталог товаров';
	isChecked: boolean = true;

	products = [];

  constructor(private dataService: DataService) { }

  ngOnInit() {
  	this.products = this.dataService.products;
  }

  changeIsChecked() {
  	this.isChecked = !this.isChecked;
  }

  editTitle() {
  	this.title = 'Новый тайтл';
  }
}

Теперь рассмотрим, что мы изменили.

В разметке первому input с типом checkbox мы сделали привязку к атрибуту checked и сразу же к событию click привязали выполнение метода changeIsChecked, который просто будет менять флаг isChecked на противоположное значение. По умолчанию isChecked = true, при клике на чекбоксе сработает метод changeIsChecked и сменит значение на false, при втором клике снова поменяет его на true.

Следующий input с типом text и кнопка также зависят от значения флага isChecked — если чекбокс отмечен (т.е. isChecked = true), тогда срабатывает привязка к атрибуту disabled и эти элементы отключаются. Как только вы снимите чекбокс, они станут доступны. Также к текстовому полю, к атрибуту есть привязка переменной с заголовком [value]='title'

Еще одна привязка в этом примере сделана к кнопке — при клике на кнопку срабатывает метод changeTitle() и наш заголовок меняется на ‘Новый тайтл’.

Таким же способом можно сделать и привязку к атрибуту [class] или [style] любого тега

  • [class]="выражение" — данная привязка заменит все классы заданные элементу
  • [class.className]="выражение" — добавляет указанный класс, можно в качестве выражения добавить функцию
  • [style.myStyle]="выражение" — придает css-свойству myStyle результат выполнения выражения, например backgroundColor (записывается именно в ‘кэмэл кейсе’, без дефиса). Нужно также указывать единицы измерения (например, px — [style.fontSize]="14px")
  • [style.myStyle.units]="выражение" — идентичная привязка, только тут можно указать единицы измерения в цели, а не в выражении. Например, [style.fontSize.px]="14"

Но для этих целей чаще всего используются директивы [ngClass] и [ngStyle], которые мы рассмотрим в следующей главе.

Двусторонняя привязка

А теперь давайте сделаем двухстороннюю привязку и будем менять заголовок на то значение, которое будем вводить в поле input.

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

Листинг 4.5 | Файл: src/app/all-products/all-products.component.html
...
<input type="text" class="form-control mr-3" 
        [(ngModel)]="title" 
        [disabled]="isChecked">
...

Далее в файле app.module.ts нам обязательно нужно подключить FormsModule

Листинг 4.6 | Файл: src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { AllProductsComponent } from './all-products/all-products.component';
import { DataService } from './shared/data.service'

@NgModule({
  declarations: [
    AppComponent,
    AllProductsComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [DataService],
  bootstrap: [AppComponent]
})
export class AppModule { }

Теперь как только вы вводите значение в поле input, у вас сразу же моментально меняется заголовок. И наоборот — если изменить значение переменной title в файле компонента, то оно будет обновлено и в шаблоне. Этого же эффекта можно было бы добиться без двухсторонней привязки, но в таком случае нам нужно было бы отлавливать событие (input) и уже в зависимости от введенного значения, менять атрибут [value]. Директива [(ngModel)] упрощает работу с привязками.

Итак, вкратце:

[атрибут/свойство]="выражение" — односторонняя привязка, данные передаются от выражения к цели (применяется для свойств и атрибутов)
{{ выражение }} — привязка со строковой интерполяцией
(событие)="выражение" — круглые скобки обозначают одностороннюю привязку к событию
[(ngModel)]="выражение" — двусторонняя привязка

Ваши вопросы и комментарии: