تحسين الـ performance في المشروع — كل التقنيات مبنية على architecture المشروع الحالي.
أكبر وأسهل تحسين ممكن تعمله.
@Component({
selector: 'app-roles',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush, // ← أضف السطر ده
...
})
export class RolesPage { }ليه مهم؟ Angular بـ Default بيعمل check على كل component في كل event. بـ OnPush بيعمل check بس لما:
- الـ
@Inputيتغير (بالـ reference) - signal داخل الـ component يتغير
- الـ component نفسه يشغّل event
قاعدة: لأن المشروع بيستخدم signal() — الـ OnPush بيشتغل تلقائياً صح معاهم.
// ❌ Variable عادية — Angular مش بيعرف امتى تتغير بدقة
roles: Role[] = [];
loading = false;
// ✅ Signal — Angular يعرف بالظبط امتى يعمل re-render
roles = signal<Role[]>([]);
loading = signal(false);وفي الـ template:
<!-- ✅ Signal تلقائياً tracked مع OnPush -->
[data]="roles()"
[loading]="loading()"<!-- ❌ بدون track — Angular بيعمل destroy/recreate لكل DOM node -->
@for (item of items; track $index) { ... }
<!-- ✅ بالـ id الحقيقي — Angular بيعمل update بس على اللي اتغير -->
@for (item of roles(); track item.id) {
<div>{{ item.name_code }}</div>
}
<app-table>عنده trackBy مدمج — الـ optimization ده بيهمك في الـ custom templates زي#cardTemplate.
بدل ما تحط array عادية كـ dummy data، حط الـ JSON في public/api/ بنفس شكل الـ envelope:
public/
└── api/
└── ships.json
└── messages.json
└── your-feature.json
{
"success": 1,
"message": { "type": "string", "texts": ["OK"] },
"result": [
{
"id": 1,
"name": "Al-Salam",
"type": "Container",
"flag": "SA",
"status": "Active",
"port": "Jeddah",
"eta": "2025-06-01T08:00:00",
"tonnage": 45000
},
{
"id": 2,
"name": "Gulf Star",
"type": "Tanker",
"flag": "AE",
"status": "Pending",
"port": "Dammam",
"eta": "2025-06-03T14:30:00",
"tonnage": 72000
}
]
}ليه كده أحسن من dummy data في الـ component؟
- بتشتغل
BaseApiServiceبالكامل (mapItem, error handling, envelope parsing) - لما الـ backend يجهز، بس بتغير الـ
urlفي الـ service — الـ component مش بيتغير خالص - الـ loading state بيشتغل صح
// ❌ مش كده
private http = inject(HttpClient);
ngOnInit() {
this.http.get<any[]>('/api/ships.json').subscribe(data => this.ships = data);
}
// ✅ كده — نفس الـ pattern الـ real service
@Injectable({ providedIn: 'root' })
export class ShipMockService extends BaseApiService<Ship> {
protected url = '/api/ships.json';
protected mapItem(raw: any): Ship {
return {
id: raw.id,
name: raw.name,
type: raw.type,
flag: raw.flag,
status: raw.status,
port: raw.port,
eta: raw.eta ? new Date(raw.eta) : null,
tonnage: raw.tonnage
};
}
}// ❌ الطريقة القديمة — محتاج ngOnDestroy يدوي
private destroy$ = new Subject<void>();
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
// ✅ الطريقة الجديدة — Angular 16+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
private destroyRef = inject(DestroyRef);
ngOnInit() {
this.svc.getAll()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(data => this.items.set(data));
}لو مش عارف تستخدم takeUntilDestroyed (لو مش في injection context)، استخدم destroy$ العادي زي ما هو موجود في الـ codebase.
المشروع عنده lazy loading بالفعل. تأكد إن كل feature جديدة بتستخدم loadComponent:
// auth.routes.ts
export const AUTH_ROUTES: Routes = [
{
path: 'roles',
loadComponent: () =>
import('./roles/pages/roles/roles').then(m => m.RolesPage)
// ↑ Angular بيحمّل الـ chunk ده بس لما المستخدم يدخل الـ route
}
];لا تعمل كده:
// ❌ static import — بيكسر الـ lazy loading
import { RolesPage } from './roles/pages/roles/roles';// ❌ بيحسب في كل render
get activeRolesCount() {
return this.roles().filter(r => r.status === 'Active').length;
}
// ✅ بيحسب بس لما roles() تتغير
activeRolesCount = computed(() =>
this.roles().filter(r => r.status === 'Active').length
);لو عندك Observable بتعرضه في الـ template:
// في الـ component
roles$ = this.svc.getAll();<!-- ✅ async pipe بيعمل unsubscribe تلقائي + بيتعمل مع OnPush صح -->
@if (roles$ | async; as roles) {
<app-table [data]="roles" ... />
}<!-- ❌ بيتستدعى في كل change detection cycle -->
<p-tag [severity]="getSeverity(item.status)" />
<!-- ✅ استخدم الـ pipe الموجود في المشروع -->
<p-tag [severity]="item.status | severity" />| الموضوع | الحل |
|---|---|
| كل component | أضف changeDetection: ChangeDetectionStrategy.OnPush |
| Local UI state | signal() بدل variables عادية |
| Loops | track item.id في @for |
| Mock data | JSON في public/api/ + BaseApiService |
| HTTP subscriptions | takeUntilDestroyed(destroyRef) |
| Features جديدة | loadComponent دايماً (مش static import) |
| Derived state | computed() بدل getters |
| Status tags | ` |
الـ Figma مش بيسمح بقراءة الداتا برمجياً بدون Figma API token. علشان تاخد الداتا منه:
-
شكل الـ Cards — شوف في الـ Figma شكل الـ card وحدد:
- الحقول اللي بتظهر (اسم، status، أرقام، إلخ)
- ترتيبها واللون بتاع الـ status
-
حط الداتا كـ Mock JSON في
public/api/your-feature.jsonبنفس الحقول اللي في الـ Figma -
اعمل Mock Service يـ extends من
BaseApiServiceوحدد الـmapItemبنفس الحقول
كده بتحاكي شكل الـ API الحقيقي من أول يوم.