Галоўная > Простая замена ngIf, ngFor у Angular v17

Простая замена ngIf, ngFor у Angular v17

angular
javascript

Не так даўно выйшла новая версія Angular v17, якая прынесла нам розныя новыя магчымасці. Хацелася б разглядзець адну з прыметных - новыя блокі патока кіравання (control flow). Гэта падыход як дазваляе прасцей зрабіць умоўны блок і цыкл у шаблонах і прыходзіць на замену *ngIf *ngSwitch *ngFor. Новы падыход працуе хутчэй па апублікаваных бенчмарках, таму пераход на новыя блокі яшчэ і паляпшае прадукцыйнасць праграмы. Сам сінтаксіс даволі інтуітыўна зразумелыя для праграмістаў, таму што менавіта так мы пішам умовы і цыклы ў кодзе. Таму давайце паглядзім як гэта ўсё працуе на простых прыкладах, параўноўваючы стары і новы сінтаксіс.

Заўвага: новы сінтаксіс мае статус "перадпрагляд распрацоўшчыкамі" (developer preview) да наступнай версіі v18. Гэта значыць, што сінтаксіс можа змяніцца. Верагоднасць гэтага невялікая, таму што самі распрацоўшчыкі ангуляра лічаць яго даволі стабільным, але яна ёсць.

Умоўныя блокі @if @else @switch @case

Раней, калі мы хацелі паказаць нейкую частку шаблона па зададзенай умове, то пісалі:

<span *ngIf="numberOfMessages > 0">
	У вас {{ numberOfMessages }} новых паведамленняў
</span>

Новы сінтаксіс выкарыстоўвае @if

@if (numberOfMessages > 0) {
	У вас {{ numberOfMessages }} новых паведамленняў
}

Новы сінтаксіс дазваляе пазбавіцца ад лішніх блокаў, якія мы дадавалі як кантэйнеры для *ngIf. Раней, калі нам было трэба каб *ngIf не генераваў аніякага блока, то выкарыстоўваўся ng-container. Зараз гэта проста не патрэбна.

Працуючы з *ngIf у нас няма ніякага else, толькі адваротная ўмова таго ж самага *ngIf. Таму гэта выглядала так:

<span *ngIf="numberOfMessages > 0">
	У вас {{ numberOfMessages }} новых паведамленняў
</span>
<span *ngIf="numberOfMessages === 0">
	У вас няма новых паведамленняў
</span>

З новым патокам кіравання гэты код можна запісаць значна прасцей!

@if (numberOfMessages > 0) {
	У вас {{ numberOfMessages }} новых паведамленняў
} @else {
	У вас няма новых паведамленняў
}

Дзіўна каб @else існавала без @else if, і ён ёсць у новым сінтаксісе. Можна пісаць доўгія ланцугі з рознымі ўмовамі:

@if (numberOfMessages > 100) {
	У вас 100+ новых паведамленняў
} @else if (numberOfMessages > 0) {
	У вас {{ numberOfMessages }} новых паведамленняў
} @else {
	У вас няма новых паведамленняў
}

Вядома, калі ў нас ёсць шмат else if дзе адна пераменная можа прымаць розныя значэнні, то гэта нагода для выкарыстання switch/case. Можна прывесці такі прыклад с класічным сінтаксісам ngSwitch:

<div [ngSwitch]="message.type">
	<span *ngSwitchCase="'SENT'">Адпраўлена</span>">
	<span *ngSwitchCase="'SEND_ERROR'">Памылка адпраўкі</span>
	<span *ngSwitchCase="'RECEIVED'">Атрымана</span>
	<span *ngSwitchDefault>Паведамленне</span>
</span>

З новым @switch @case гэта будзе выглядаць так:

@switch (message.type) {
	@case ('SENT') { Адпраўлена }
	@case ('SEND_ERROR') { Памылка адпраўкі }
	@case ('RECEIVED') { Атрымана }
	@default { Паведамленне }
}

Выдатная і зразумелая замена атрымалася, якая дазваляе прасцей пісаць, а галоўнае чытаць, напісаны код.

Бонусам хацелася бы дадаць, што ў *ngIf нам даволі часта трэба атрымаць значэнне з Observable, якое не хочацца кожны раз прапускаць праз async пайп. З *ngIf мы пісалі гэта так

<section *ngIf="message$ | async; let message">
    <div class="subject">{{ message.subject }}</div>
    <div class="body">{{ message.body }}</div>
</section>

Такая магчымасць засталася і з новым @if з дапамогай as

@if (message$ | async; as message) {
    <section>
        <div class="subject">{{ message.subject }}</div>
        <div class="body">{{ message.body }}</div>
    </section>
}

Цыкл @for

Як вы ўжо здагадаліся новы сінтаксіс выкарыстоўвае @ для абазначэння новых блокаў з абазначэннем іх меж з дапамогай фігурных дужак { }. Таму прыкладна так і выглядае новы цыкл @for.

Тое што раней мы запісвалі так:

<section *ngFor="let message of messages$ | async; trackBy: trackByFn">
    <div class="subject">{{ message.subject }}</div>
    <div class="body">{{ message.body }}</div>
</section>

зараз выглядае вось так:

@for (message of messages$ | async; track message.id) {
    <section>
        <div class="subject">{{ message.subject }}</div>
        <div class="body">{{ message.body }}</div>
    </section>
}

Тут можна заўважыць, што раней мы павінны былі заўсёды пісаць функцыю для trackBy, а зараз мы проста можам указаць па якой уласцівасці аб'екта рабіць трэкінг. У дадзеным выпадку ўнікальны параметр message гэтаid, і мы напісалі track message.id.

Новай магчымасцю з'яўляецца ўказанне што рабіць калі элементаў у масіве няма. Робіцца гэта з дапамогай @empty блока:

@for (message of messages$ | async; track message.id) {
    <section>
        ...
    </section>
} @empty {
  Няма паведамленняў
}

Усе гэтыя невялічкія дадаткі робяць стварэнне ўмоў і цыклаў у шаблонах больш прасцейшымі і больш зразумелымі.

Міграцыя на новы сінтаксіс патока кіравання

Вам ужо хочацца паспрабаваць новы сінтаксіс у старым праекце, які вы толькі што мігравалі на v17? Каманда ангуляра падумала і аб гэтым. Для таго каб зрабіць аўтаматычную міграцыю на новы сінтаксіс на старым праекце, дастаткова запусціць

ng generate @angular/core:control-flow

Гэтая каманда зробіць усё за вас.

Спасылкі

Дарэчы, з 17-й версіі ангуляр пераехаў на новы веб-сайт angular.dev

P.S.

Гэта мой першы досвед напісання артыкулаў на гэтым партале (і ўвогуле не тое каб шмат я пісаў раней :) ), так што буду ўдзячны за любыя парады, выпраўлення і інш. Можна пісаць у асабістыя ў мастадон, ці пад гэтым допісам. Або на пошту editor[слімак]tomin[кропка]slmail[кропка]me. Дзякуй што пачыталі!

Siarhei Tomin, 2023-12-02
Каментары

    (Каб даслаць каментар залагуйцеся ў свой уліковы запіс)

    ;