Оновлений стиль коду для Angular

В Angular останнім часом з’явилося багато нового: від сигналів до оновленого стилю для шаблонів. Тож я подумав, можливо, настав час поділитися оновленим підходом до code style?

pic

Standalone

З випуском 19-ї версії всі компоненти, які ви створюєте, за замовчуванням стають standalone.

Якщо у вас версія нижче 19, я рекомендую створювати виключно standalone-компоненти, уникаючи використання модулів.

 // Standalone-компонент для версій Angular < 19  
@Component({  
 standalone: true,  
 selector: 'my-component',  
 template: `Hello World!`  
})  

// Standalone-компонент у Angular 19  
@Component({  
 // прапор standalone більше не потрібен  
 selector: 'my-component',  
 template: `Hello World!`  
})

Порада! У Angular 19 з’явився прапор компілятора, який видасть помилку, якщо виявить компонент, директиву або пайп, що не є standalone.

{  
 "angularCompilerOptions": {  
 "strictStandalone": true  
 }  
}

Signals

Ну тут все зрозуміло: стараємось більше використовувати сигнали, computed, а також input() та output().

Ви ж розумієте, що команда фреймворка рухається в бік відмови від Zone.js, тому чим більше ви будете використовувати сигнали, тим менше потрібно буде рефакторити код у майбутньому.

Нова ера Lifecycle

Попрощайтеся з ngOnInit та ngAfterViewInit і оберіть більш спрощений підхід!

pic

Ось базовий приклад як було раніше з хуком ngOnChanges:

@Component({  
 standalone: true,  
 selector: 'is-even',  
 template: `
Is Even: {{ isEven }}
`,    changeDetection: ChangeDetectionStrategy.OnPush   })   export class IsEvenComponent implements OnChanges {    isEven: boolean | undefined;    @Input({ required: true }) counter!: number;       ngOnChanges(changes: SimpleChanges): void {    if (changes['counter']) {    this.isEven = changes['counter'].currentValue % 2 === 0;    }    }   } 

Тепер як це написати по новому:

@Component({    
 standalone: true,    
 selector: 'is-even',    
 template: `
Is Even: {{ isEven() }}
`,    
 changeDetection: ChangeDetectionStrategy.OnPush   
})   
export class IsEvenComponent {    
 counter = input.required();    
 isEven = computed(() => this.counter() % 2 === 0);   
}

Inject

Раніше для того щоб заюзати наш DI, потрібно було в конструкторі прописувати залежності, тепер у нас є новий метод inject!

 // Раніше  
constructor(private userService: UserService) {}  

// Зараз  
private readonly userService = inject(UserService) 

Що він дає:
- inject зберігає правильний тип при використанні токенів
- inject можна використовувати в інших функціях, що дає змогу створювати композиційні функції
- При спадкуванні немає потреби передавати впроваджені сервіси до батьківського класу через виклик super в конструкторі.

Ін’єкція через конструктор залежить від прапора useDefineForClassFields, який потрібно явно встановити в значення false

Constructor

Окей, ми зрозуміли, що конструктор більше не є необхідним для inject. Але що ж робити з хуками afterRender та afterNextRender? А як щодо ефектів? Адже в документації зазначено, що їх потрібно інітити саме в конструкторі!?

@Component({ /* ... */ })   
export class MyComponent {       
 private readonly logProductChanges = effect(() => { ...});       
 private readonly afterNextRenderRef = afterRender(() => { ...});

});  

 private readonly afterNextRender = afterNextRender(() => {...});  

}

Бам! Такий підхід робить ваш код чистішим і організованішим.

RxJS

А що робити з кодом, який написаний на стрімах?

Просто конвертуємо його в сигнали!

@Component({  
 template: `{{ someValue() }}`  
})  
export class MyComponent {  
 someValue = toSignal(someStream$);  
}

Шаблони

Раніше ми використовували директиви на кшталт ngIf, ngFor, ng-template для роботи з умовними виразами та рендерингом шаблонів.

ngIf

// Раніше  

Content is visible
    Placeholder            
// Зараз   
@if(isVisible) {    
    Content is visible   
}@else {    
    Placeholder   
}

ngFor

// Раніше   
{{ item }}

// Зараз   
@for (let item of items; track: item.id) {    
    {{ item }}
}

let

// Раніше   
Hello, {{user.name}}
    {{snack.name}}    
    Update profile   

// Зараз   
@let user = user$ | async;      
@if(user) {    
    Hello, {{user.name}}
    @for (snack of user.favoriteSnacks; track snack.id) {    
        {{snack.name}}
    }    
    Update profile   
}

Висновки

Просто дотримуйтесь цих порад, і буде вам щастя! 😄

Перекладено з: Оновлений Code Style для Angular

Leave a Reply

Your email address will not be published. Required fields are marked *