کافه انگولار
133 subscribers
49 photos
7 files
39 links
توی کافه انگولار، Angular رو مثل روتین روزمره یاد می‌گیریم؛ پروژه‌محور، تمیز، و قابل استفاده تو کار واقعی
اینجا Performance مهمه
رو Best practice ها کار میکنیم

ادمین: @omidkhosrojerdi
لینکدین: linkedin.com/in/omidkhosrojerdi
Download Telegram
برای استفاده اصولی و بهینه از Angular signal بهتره راه اصولی و ابزار های درست رو استفاده کنیم تا performance بهتر و memory leak پایین تری داشته باشم

این اینفوگرافیک بصورت خیلی ساده نشون میده که برای هر بخش از پروژه بهتره از چه ابزاری استفاده کنیم

#angularsignal
#statemanagement
@angularcafe
👍2
تفاوت computed و linkedsignal خیلی ساده است
خیلی خودمونی بخوام بگم مثل این میمونه:
توضیح computed مثل نمایش درصد شارژ باتری موبایل میمونه
توضیح linkedSignal مثل نمایش دمای کولر ماشین که زده ۲۴ بر اساس سنسور های ماشین ولی شما میتونین بذاریدش رو ۲۰
در واقع computed یک محاسبه کننده read-only هست
و linkedSignal یک پیشنهاد کننده ولی قابل دستکاری هست

#angularsignal
#angular
@angularcafe
👍2
Angular-signal-forms.pdf
2.2 MB
کتاب رایگان Angular signal form از مجموعه Angular love رو استفاده کنید برای افزایش دانش تون خیلی بدرد میخوره

#ebook
#angularsignal
#angular
@angularcafe
👍2
یکی از علایق شدیدم تو پنل ها استفاده از Chart های قشنگ و کاربردیه که چشم هر کارفرمایی رو پر میکنه
یه نمونه signal based با plotyjs زدم که نمونه خوبی شده

البته کتابخونه خیلی قوی نیست ولی بازم چارت های به نسبت خوبی داره

https://github.com/omidkh68/angular-21-plotyjs

#angularchart
#angularsignal
#angular
@angularcafe
👍2
یه بار برای همیشه با کدهای اضافی موقع submit فرم توی Angular Signal Forms خداحافظی کنیم

قابلیت signal form که اضافه شد خیلی خفن و قوی بود مثل مدل قبلی نبود که درگیر ماژول و چیزای دیگه بشیم ولی هنوز خوب پخته نشده بود و یه سری کارا رو باید دستی مدیریت میکردیم مثل novalidate کردن برای غیر فعال کردن validation دستی خود browser ها
یا جلوگیری از reload شدن صفحه با (submit)
حالا تو نسخه بعدی که قراره نسخه 21.2 باشه یه directive اضافه دارن میکنن که به خود form اضافه میشه

<form [formRoot]="loginForm">


این Directive هم خودش از novalidate پشتیبانی میکنه و جلوی اعتبار سنجی پیشفرض مرورگر رو میگیره
هم جلوی reload شدن صفحه رو میگیره
هم submit رو هندل میکنه

البته این قابلیت فعلا بصورت Experimental اضافه میشه تا تثبیت بشه و به برنچ اصلی مرج بشه

لینک commit:

https://github.com/angular/angular/commit/ba009b6031

#signalform
#angularsignal
#angular
@angularcafe
1
اگه خواستید یک current route رو مقایسه کنید به صورت signal based دیگه به جای استفاده از router.isActive از signal based function به عنوان isActive که از پکیج @angular/router هست استفاده کنیم

<div> {{ isProfileActive() }} </div>

روش قدیم و deperacated شده:

‍‍
this.#router.isActive('/user/profile', {
paths: 'exact',
queryParams: 'ignored'
});

// Signal-based

readonly isProfileActive = isActive(
'/user/prifile',
this.#router,
{
paths: 'exact',
queryParams: 'ignored'
}
);


#angularsignal
#angular
@angularcafe
1👍1
میدونستید اگه یک component رو میخواستین با reactive form بصورت custom field در بیارید باید کلی تنظیمات با ControlValueAccessor یا به اختصار CVA رو داخل component تون extend کنین؟

حالا با صدقه سری signal form و کلاس FormValueControl که generic هم هست میتونین با یه model خیلی ساده اون component رو به form تون اضافه کنین و ازش به عنوان field فرم تون استفاده کنید
فقط کافیه به component تون directive مربوط به فیلد رو اختصاص بدید

<SomeComponent [formField]="someName"/>


#angularsignal
#signalform
@angularcafe
🔥2
یه plugin تقویم یا Datepicker جدید اومده به اسم NGXSMK datepicker که signal friendly هست

از RTL و localization هم ساپورت میکنه

اگه استفاده کردید تجربه تون رو به اشتراک بذارین تو Discussion

https://ngxsmk.github.io/ngxsmk-datepicker/

#angularsignal
#datepicker
@angularcafe
3👍1
یه نمونه signal form و logic structure با اسم گذاری جدید انگولار داخل repo اضافه کردم

https://github.com/angularcafeacademy/angular-signal-form

دمو:
https://angularcafeacademy.github.io/angular-signal-form/

اگه نکته ای توش به مشکل خوردید نظر بدید ✌🏻


#angularsignal
@angularcafe
👍3
🛜 سیگنال، بهترین حرکت انگولار

تا همین چند وقته پیش برای یه کار ساده مثل toggle کردن دکمه sidebar‌ای چیزی از rxjs استفاده میکردیم و کلی پیچیدگی‌های مختلف داشتیم که یه کار خیلی پیش‌پا افتاده و ساده انجام بدیم.

تو سال ۲۰۲۶ دیگه این چیزا مهم نیست و صرفا باید بذاریمشون پشت سر و پیچیدگی هارو کم کنیم.

دیگه بهترین state management برای انگولار signal هست و باید به بهترین شکل ازش استفاده کنیم.

کار یه نیرو خبره و سنیور باید این باشه بتونه تو پروژه های بزرگ:
‍‍۱. پیچیدگی هارو کم کنه
۲. پرفورمنس رو ببره بالا
۳. بالاترین reactivity رو تو برنامه بوجود بیاره
۴. بتونه memory leak رو به کمترین حد خودش برسونه

از مشکلات تون تو سیگنال بگید دورهم حل اش کنیم

#angularsignal
@angularcafe
👍51
میدونین Zone.js تو Angular مثل یه نگهبان فضوله 😅
‏هر اتفاق async (کلیک، HTTP، تایمر، Promise) می‌افته، می‌پره وسط و میگه: «یه چیزی شد!»
‏بعد Angular هم global change detection رو روی کل برنامه می‌ندازه و کل component tree رو دوباره چک می‌کنه.
‏این برای Developer ساده‌ست، ولی برای runtime دقیق/بهینه نیست.

‏Zoneless Angular میگه: به‌جای «یه چیزی شد… همه‌چی رو چک کن» برو سمت «state عوض شد… فقط همون قسمت رو آپدیت کن».
در ‏نتیجه:
پس zone.js استفاده بشه ساده است همه چی ولی بهینه نیست
و zoneless اگه استفاده بشه، دقت و performance میره بالا
البته با zoneless کردن صرفا track changes ها حذف نمیشه، یعنی Angular داره از automatic global tracking میره سمت explicit reactive state (مثل signals).

پس اگه پروژه تون خیلی پیچیده و بزرگ نیست و میتونین zoneless‌اش کنید بعد متوجه performance بالا و رندرینگ بهترش میشید

#zoneless
#angularsignal
@angularcafe
3👍2
یادتونه وقتی میخواستیم یه input رو focus کنیم تو reactive form با viewChild اینکار رو میکردیم یا حتی از viewChildren یه خونه از آرایه شو میگرفتیم و focus میکردیم؟

حالا تو Signal form بهمون این قابلیت داده شده که یه فیلد از فرم رو با متد focusBoundControl بیایم focus کنیم 🤩

مثلا میتونیم بگیم اگه فلان فیلد پر شد برو فیلد بعدی یا حتی خطا هایی که میخوریم رو فیلد شو focus کن

protected nextInvalidField() {
const nextInvalidField = this.form().errorSummary()[0];
if (nextInvalidField) {
nextInvalidField.fieldTree().focusBoundControl();
}
}


اگه کار نکرد یه request animation frame میزنیم تا زمان اجرا رو بندازه به فریم بعدی

clearField(field: string | number): void {
this.formModel.update(fields => ({...fields, [field]: ''}))

requestAnimationFrame(() => {
this.form.someField().focusBoundControl();
});
}

#angularsignal
#signalform
@angularcafe
🔥21👍1
انگار Angular بالاخره داره وارد یه دوره‌ی جدید میشه:

یکی از جذاب‌ترین چیزهایی که با Angular 22 قراره بیشتر جدی بشه، حرکت به سمت Signal Forms هست.
یعنی کم‌کم از این مدل‌ها فاصله می‌گیریم :
FormBuilder
FormGroup
FormControl
valueChanges
patchValue
و می‌ریم سمت یه ساختار تمیزتر، ساده‌تر و reactiveتر :
public user = signal({
name: '',
email: ''
});
public form = form(this.user);

ماجرا فقط کوتاه‌تر شدن syntax نیست:
boilerplate کمتر
خوانایی بهتر
مدیریت state ساده‌تر
معماری Signal-first
تجربه توسعه‌دهندگی بهتر

Angular دوباره داره حس مدرن بودن میده

اگه سوالی تو زمینه signal و signal form و مابقی مسایل مربوط به signal داشتین خوشحال میشم کمکی کنم بهتون 👌🏾

#angularsignal
#signalform
#angularcafe
👍52
import { Component, signal } from '@angular/core';
import { httpResource } from '@angular/common/http';

@Component({
selector: 'app-user',
template: `
@if (user.isLoading()) {
<p>Loading…</p>
} @else if (user.hasValue()) {
<h1>{{ user.value().name }}</h1>
}
`
})
export class UserComponent {
userId = signal(1);
// Re-fetches automatically whenever userId changes.
user = httpResource<User>(() => `/api/users/${this.userId()}`);
}


استفاده از httpResource بسیار جذاب و کارامد هست 🥳

#angularsignal
#angularcafe
👍4👌21
https://ui.ng.guide/ui

x.com/katsuba_igor

یکی از توسعه دهنده های انگولار یه wrapper برای material 3 زده که هم signal first هست هم zoneless

و اینکه به نسبت خود سایت انگولار هم مستندات بهتری داره هم شخصی سازی بهتری میشه رو component ها داشت

بدون css framework و سبک درست شده

#angularmaterial
#angularsignal
#angularcafe
👍21👏1
کافه انگولار
Photo
با stable شدن API های Resource و httpResource برای مدیریت request‌ها، loading state و error state ها، برای component‌ها و service‌ها خیلی کاربردی شده.

ولی این API های جدید برای Router Guard و Resolver ها مناسب نیستن!

حالا چرا؟

چون ذاتا Guard‌ها و Resolver‌ها تا مشخص شدن نتیجه شون Navigation رو متوقف میکنن، یعنی باید منتظر یه نتیجه ای مثل Boolean، Promise یا Observable بمونن.

ولی Resource‌ها ذاتا Reactive هستن و وقتی داخل Resolver و یا Guard ازشون استفاده کنیم مقدار اولیه value شون معمولا undefined هست و request در وضعیت loading قرار داره و ما اگه همون لحظه بخوایم value شون رو استفاده کنیم اون Guard ممکنه سریعا مقدار false رو برگردونه و Navigation رو Cancel کنه حتی قبل از اینکه API اصلا جواب رو برگردونه!

export const authGuard: CanActivateFn = () => {
const authResource = httpResource<boolean>(() => ({
url: '/api/auth/status',
}));

// This returns the signal's current value synchronously
// Which is `undefined` initially!
return authResource.value() ?? false;
};



حالا راه درست چیه؟

برای Guard و Resolver فعلا همچنان از HttpClient و Observable یا Promise استفاده میکنیم که بشه از Async بودن شون این بهره رو برد تا جواب حاصل نشده، نتیجه ای برگردونده نشه قبل از Navigation کامل.

export const authGuard: CanActivateFn = () => {
const http = inject(HttpClient);

// An Observable cleanly blocks the Router until it completes or emits
return http.get<boolean>('/api/auth/status');
};



پس برای جمع‌بندی در حال حاضر Resource و httpResource برای UI و Reactive Data عالی هستن، اما برای Guard و Resolver درست هست که از همون روش‌های فعلی Observable و Promise استفاده کنیم.

در واقع Resource برای Data binding استفاده بشه و Observable و Promise برای Control flow در Routing پروژه.

#angularsignal
#angularcafe
👍4
کافه انگولار
Photo
خبر خوب اینکه تو Dialog در Angular Material 22 قابلیت input binding اضافه شده.

یعنی چی؟

قبلا برای پاس دادن مقادیری به Mat Dialog باید data رو پر میکردیم و داخل Component مورد نظر با inject کردن MAT_DIALOG_DATA اونو میگرفتید و استفاده میکردیم

حالا برای اینکه بتونیم از input و output و two-way binding استفاده کنیم قابلیت binding اضافه شده و میشه عین یه component که داخل html مینویسیم و بهش input و output پاس میدیم با component پاس داده شده به Dialog هم همون مدلی برخورد کنیم:

برای مثال:

code:
protected openSettingsDialog() {
this.dialog.open(NotificationSettingsComponent, {
width: '28rem',
panelClass: 'settings-dialog',
data: {user: this.user()}
});
}


و با گرفتن MAT_DIALOG_DATA اون دیتا رو استفاده میکردیم.

حالا با قابلیت binding میتونیم همچین چیزی انتظار داشته باشیم:

code:
<app-notification-settings
[user]="user()"
[(enabled)]="notificationsEnabled"
(saved)="saveNotificationSettings($event)"
/>


حالا چطوری؟

code:

protected openSettingsDialog() {
this.dialog.open(NotificationSettingsComponent, {
width: '28rem',
panelClass: 'settings-dialog',
bindings: [
inputBinding('user', this.user),
outputBinding<NotificationSettingsSaved>('saved', settings => {
this.saveNotificationSettings(settings);
}),
twoWayBinding('enabled', this.notificationsEnabled),
],
});
}


داخل component ای هم که داخل Dialog مد نظر داریم مثل حالت عادی signal input و outout و two-way رو استفاده میکنیم:

code:
export class NotificationSettingsComponent {
readonly user = input<User | null>(null);
readonly enabled = model(false);
readonly saved = output<NotificationSettingsSaved>();
protected save() {
if (this.user()) {
this.saved.emit({
userId: this.user()!.id,
enabled: this.enabled(),
});
}
}
}


منبع

#angularsignal
#angularcafe
3👏2
۱. مهاجرت‌های خودکار

ng generate @angular/core:signal-input-migration

ng generate @angular/core:output-migration

ng generate @angular/core:signal-queries-migration


۲. برای تبدیل Observableها از bridge مربوطه یعنی toSignal() استفاده کنید.

// Before
data$ = this.http.get(this.API_URL);
// Converting to Signal
data = toSignal(this.data$);


نمونه بالا مقدار اولیه رو undefined ست میکنه که میشه مثل نمونه زیر با initialValue مقداردهی اولیه کرد signal مون رو:

// Without default value
rates: Signal<Rates | undefined> = toSignal(this.rates$);
// With default value
rates: Signal<Rates> = toSignal(this.rates$,
{initialValue: {USD: 1, EUR: 1, GBP: 1}}
);


۳. تبدیل Subject‌ها به Signal:

// Subject version
currentCurrency = new BehaviorSubject<CurrencySymbol>(DEFAULT_CURRENCIES[0]);
// Signal version
currentCurrency = signal<CurrencySymbol>(DEFAULT_CURRENCIES[0]);


۴. جایگزین RXJS Operator‌ها با Computed:

// RxJs code
currentExchangeRate$ = combineLatest([
this.exchangeRates$,
this.currentCurrency$
]).pipe(
map(([rates, current]) => rates[current.code as keyof ExchangeRates])
);

// Equivalent Signal based version
currentExchangeRate = computed(
() => this.exchangeRates()[this.currentCurrency().code]
);


بعضی موقع‌ها، می‌خوایم بخشی از منطق RXJS رو نگه داریم چون خوب کار می‌کنه و از یه سری ویژگی‌هایی که معادل مستقیم مبتنی بر سیگنال ندارن مثل نمونه زیر که timer هست استفاده کنیم. تو این حالت، toSignal باز هم میتونه به عنوان یه Bridge عمل کنه و کل عملیات RX مون رو به signal تبدیل کنه:

// RxJs code
exchangeRates$: Observable<ExchangeRates> = timer(0, 3600000)
.pipe(
switchMap(() => this.http.get<any>(this.API_URL)),
map(data => ({
USD: 1,
EUR: data.rates.EUR,
GBP: data.rates.GBP
}))
);

// Simple Signal conversion with toSignal()
exchangeRates = toSignal(this.exchangeRates$);


نتیجه‌گیری به نظرم اینه که با جفت ویژگی ها تو صلح باشیم و از هرکدوم تو جای خودش استفاده کنیم و اگه از RXJS استفاده میکنیم از Memory leak جلوگیری کنیم و ازش اجتناب کنیم و البته که استفاده غیراصولی از Signal هم میتونه side effect‌های Memory leak داشته باشه ولی مدیریتش راحت‌تر از RX هست و همچنین لذت استفاده از signal به نظرم، باعث جلوگیری از استفاده چندین operator و کم کردن تعداد boilerplate‌های زیادی که قبلا مینوشتیم رو به ارمغان میاره برامون.

#angularsignal
#angularcafe
👍31