میدونیم که «بایندینگ دوطرفه» در «انگولار» با استفاده از "ngModel" کار راحتیه. به این صورت که اگه متغیری به اسم username رو تو کامپوننت مربوطه تعریف کرده باشیم، برای بایندینگ دوطرفهی اون در سمت template مینویسیم:
<input [(ngModel)]="username" />
حالا اگه ورودی این input تغییر کنه مقدارش به متغیر username منتقل میشه و هرجای برنامه هم که مقدار این متغیر رو تغییر بده، مقدار جدید توی input نمایش داده میشه.
اما گاهی نیاز میشه که بایندینگ دوطرفه رو روی آرایهای از متغیرها اجرا کنیم. فرض کنیم آرایهای به نام cities داریم:
let cities = ['Tehran','Rasht','urmieh'];
نمایش عناصر این آرایه بوسیلهی input ها ساده است:
<div *ngFor="let item of cities"> <input [(ngModel)]="item"> </div>
همونطور که مشخصه در اینجا هم از ngModel برای بایندینگ دوطرفه استفاده شده، اما اگه این کد رو اجرا کنیم نتیجهی دلخواه به دست نمیاد. یعتی اگه کاربر مقدار یک یا چندتا از input ها رو تغییر بده، مقادیر در آرایهی cities تغییر نمیکنن. برای رفع این مشکل باید تغییرات زیر رو در کد بالا اعمال کنیم (بخش پر رنگ کد):
<div *ngFor="let item of cities;let index = index;"> <input [(ngModel)]="cities[index]"> </div>
یعنی به جای متغیر item از اندیسهای آرایه برای مقداردهی به ngModel استفاده میکنیم. به این ترتیب مشکل قبلی حل میشه، ولی در زمان اجرا کاربر در تغییر مقدار input مشکل خواهد داشت. فرض کنید کاربر میخواد مقدار urmieh (عنصر سوم آرایه) رو به urmia تغییر بده، تصمیم میگیره اول eh رو از انتهای کلمه پاک کنه و بعد a رو اضافه کنه. ولی همینکه h رو پاک میکنه، مکاننما (curser) از input مربوطه خارج میشه، در واقع focus از روی input برداشته میشه و کاربر باید دوباره روی اون input کلیک کنه تا بتونه تغییر بعدی رو اعمال کنه. این روند با هر تغییری در ورودیها تکرار میشه.
علت اینه که با هر تغییر، انگولار مجددا DOM رو بازسازی میکنه و به این ترتیب focus روی input قبلی ازدست میره. برای رفع این مشکل، ابتدا تابعی رو تو کامپوننت مربوطه به صورت زیر تعریف میکنیم:
trackByIndex(index: number, item: any) { return index; }
و مجددا کد مربوط به حلقهی ngFor رو تغییر میدیم (بخش پر رنگ اضافه شده):
<div *ngFor="let item of cities;let index = index;trackBy:trackByIndex;"> <input [(ngModel)]="cities[index]"> </div>
در اینجا ما از ویژگی trackBy در حلقهی ngFor استفاده کردیم. به طور کلی، استفاده از این ویژگی یکی از توصیهها برای افزایش کارایی در استفاده از ngFor در انگولار هستش، بخصوص وقتی تعداد عناصر حلقه زیاد و امکان تغییر اونها توسط کاربر وجود داشته باشه. تابعی که به این ویژگی نسبت داده میشه (ما اسمش رو trackByIndex گذاشتیم) همیشه دو پارامتر ورودی میگیره، اندیس عنصر مربوطه در آرایه و خود عنصر، و باید یه مقدار منحصر به فرد برای هر عنصر برگردونه تا انگولار بتونه با استفاده از اون فقط عنصری که مقدارش تغییر کرده رو بروز کنه و نیاز به بازسازی کل DOM نداشته باشه. ما در مثال بالا اندیس عنصر در آرایه رو بر میگردونیم.