Routing In ANGULAR 2+
Routing In ANGULAR 2+
جدول المصطلحات:
ما هو مصطلح التوجيه (Routing):
التوجيه (Routing) في Angular هو نظام يتيح إنشاء تطبيقات وحيدة الصفحة (SPA - Single Page Applications) حيث يتم التنقل بين المكونات المختلفة دون إعادة تحميل الصفحة. يتم التحكم في عرض المكونات بناءً على URL الحالي في المتصفح.
يتم توفير نظام التوجيه من خلال وحدة @angular/router والتي يجب استيرادها في التطبيق.
إعداد وتكوين التوجيه:
1. استيراد وحدة التوجيه
typescript
import { RouterModule, Routes } from '@angular/router';
2. تعريف المسارات (Routes)
المسار (Route) هو كائن JavaScript يحتوي على خاصيتين رئيسيتين:
path: سلسلة تحدد مسار URL
component: المكون الذي يجب عرضه لهذا المسار
typescript
// في app.module.ts
const routes: Routes = [
{ path: 'crisis-list', component: CrisisListComponent },
{ path: 'heroes-list', component: HeroesListComponent },
];
@NgModule({
imports: [
BrowserModule,
RouterModule.forRoot(routes) // تسجيل المسارات
],
exports: [RouterModule]
})
export class AppModule { }
3. إنشاء AppRoutingModule (أفضل ممارسة)
ينصح بإنشاء وحدة منفصلة للتوجيه:
typescript
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CrisisListComponent } from './crisis-list.component';
import { HeroesListComponent } from './heroes-list.component';
const routes: Routes = [
{ path: 'crisis-list', component: CrisisListComponent },
{ path: 'heroes-list', component: HeroesListComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
استخدام RouterOutlet:
<router-outlet> هو موجه (Directive) يعرض المكون المطابق للمسار الحالي.
مثال:
html
<!-- app.component.html -->
<app-header></app-header>
<!-- سيتم عرض المكون المطابق للمسار هنا -->
<router-outlet></router-outlet>
<app-footer></app-footer>
بدلاً من عرض جميع المكونات معاً:
html
<!-- ❌ غير صحيح -->
<app-crisis-list></app-crisis-list>
<app-heroes-list></app-heroes-list>
نستخدم:
html
<!-- ✅ صحيح -->
<router-outlet></router-outlet>
التنقل باستخدام RouterLink:
1. الروابط الأساسية
html
<nav>
<a routerLink="/crisis-list">Crisis Center</a>
<a routerLink="/heroes-list">Heroes</a>
</nav>
2. تحديد المسار النشط بـ RouterLinkActive
html
<nav>
<a routerLink="/crisis-list"
routerLinkActive="active-link">
Crisis Center
</a>
<a routerLink="/heroes-list"
routerLinkActive="active-link">
Heroes
</a>
</nav>
3. RouterLinkActive مع exact match
html
<a routerLink="/"
routerLinkActive="active"
[routerLinkActiveOptions]="{ exact: true }">
Home
</a>
مسارات خاصة:
1. إعادة التوجيه (Redirect)
typescript
const routes: Routes = [
{ path: '', redirectTo: '/heroes-list', pathMatch: 'full' },
{ path: 'crisis-list', component: CrisisListComponent },
{ path: 'heroes-list', component: HeroesListComponent },
];
redirectTo: المسار الذي سيتم التوجيه إليه
pathMatch:
'full': يتطابق مع المسار الكامل
'prefix': يتطابق مع بداية المسار
2. صفحة 404 (Wildcard Route)
typescript
const routes: Routes = [
{ path: '', redirectTo: '/heroes-list', pathMatch: 'full' },
{ path: 'crisis-list', component: CrisisListComponent },
{ path: 'heroes-list', component: HeroesListComponent },
{ path: '**', component: PageNotFoundComponent } // Wildcard - يجب أن يكون آخر مسار
];
ملاحظة مهمة: يجب أن يكون مسار Wildcard (**) دائماً في نهاية المصفوفة لأن Angular يطبق المسارات بالترتيب ويستخدم أول تطابق.
Route Parameters:
1. تعريف المسار مع بارامتر
typescript
const routes: Routes = [
{ path: 'heroes', component: HeroesListComponent },
{ path: 'hero/:id', component: HeroDetailComponent }, // :id هو بارامتر
];
2. التنقل مع بارامتر
html
<!-- في القالب -->
<a [routerLink]="['/hero', hero.id]">{{ hero.name }}</a>
typescript
// في المكون TypeScript
this.router.navigate(['/hero', heroId]);
3. قراءة البارامتر في المكون
الطريقة 1: Snapshot (للقيم الثابتة)
typescript
import { ActivatedRoute } from '@angular/router';
export class HeroDetailComponent implements OnInit {
constructor(private route: ActivatedRoute) {}
ngOnInit() {
const id = this.route.snapshot.paramMap.get('id');
// استخدام id...
}
}
الطريقة 2: Observable (للقيم الديناميكية)
typescript
ngOnInit() {
this.route.paramMap.subscribe(params => {
const id = params.get('id');
// يتم تحديث id عند تغيير المسار
});
}
Query Parameters و Fragments:
1. تمرير Query Parameters
html
<a [routerLink]="['/search']"
[queryParams]="{ q: 'angular', page: 1 }">
Search
</a>
typescript
// برمجياً
this.router.navigate(['/search'], {
queryParams: { q: 'angular', page: 1 }
});
2. قراءة Query Parameters
typescript
this.route.queryParamMap.subscribe(params => {
const searchQuery = params.get('q');
const page = params.get('page');
});
3. Fragments (الروابط الداخلية)
html
<a [routerLink]="['/document']" fragment="section-2">
Go to Section 2
</a>
Child Routes (المسارات الفرعية):
تعريف:
typescript
const routes: Routes = [
{
path: 'admin',
component: AdminComponent,
children: [
{ path: 'users', component: UsersComponent },
{ path: 'settings', component: SettingsComponent },
{ path: '', redirectTo: 'users', pathMatch: 'full' }
]
}
];
في القالب:
html
<!-- admin.component.html -->
<h2>Admin Panel</h2>
<nav>
<a routerLink="users">Users</a>
<a routerLink="settings">Settings</a>
</nav>
<router-outlet></router-outlet> <!-- للمسارات الفرعية -->
Route Guards (حمايات المسارات):
1. CanActivate - للتحقق قبل الدخول للمسار
typescript
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(): boolean {
if (this.authService.isLoggedIn()) {
return true;
}
this.router.navigate(['/login']);
return false;
}
}
2. استخدام Guard
typescript
const routes: Routes = [
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [AuthGuard] // حماية المسار
}
];
Lazy Loading (التحميل الكسول)
تعريف:
typescript
const routes: Routes = [
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)
}
];
في AdminModule:
typescript
const adminRoutes: Routes = [
{ path: '', component: AdminDashboardComponent },
{ path: 'users', component: UsersComponent }
];
@NgModule({
imports: [RouterModule.forChild(adminRoutes)],
exports: [RouterModule]
})
export class AdminRoutingModule { }
أفضل الممارسات
إنشاء AppRoutingModule منفصل عن AppModule
ترتيب المسارات من الأكثر تحديداً إلى الأقل تحديداً
استخدام Lazy Loading للمسارات الكبيرة
تطبيق Route Guards للمسارات المحمية
معالجة الأخطاء مع صفحة 404
استخدام Relative Navigation داخل Child Routes
فصل منطق التوجيه عن منطق الأعمال
مثال كامل متكامل
typescript
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './auth.guard';
const routes: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'login', component: LoginComponent },
// Lazy Loading
{
path: 'products',
loadChildren: () => import('./products/products.module').then(m => m.ProductsModule)
},
// Protected Route
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [AuthGuard]
},
// 404 Page
{ path: '**', component: PageNotFoundComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes, {
enableTracing: false, // للت debugging
scrollPositionRestoration: 'enabled'
})],
exports: [RouterModule]
})
export class AppRoutingModule { }
ليست هناك تعليقات: