تطوير تطبيقات الويب باستخدام Angular


الدرس: إدارة الابحار مع التوجيه


الصفحة السابقة
واحدة من المزايا الكبيرة لاستخدام Angular هي القدرة على إنشاء "تطبيقات صفحة واحدة" (SPA). على الويب ، هذه التطبيقات سريعة وسلسة: لا يوجد سوى تحميل صفحة واحدة في البداية ، وحتى إذا كانت البيانات تستغرق أحيانًا وقتًا للوصول ، فإن الإحساس للمستخدم هو شعور التطبيق عادي. بدلاً من تحميل صفحة جديدة مع كل نقرة أو كل تغيير في عنوان URL ، فإننا نستبدل محتوى أو جزء من محتوى الصفحة: نقوم بتعديل المكونات المعروضة هناك ، أو محتوى هذه المكونات. نقوم بإنجاز كل هذا باستخدام "التوجيه routing" ، حيث يقرأ التطبيق محتوى URL لعرض المكون (المكونات) المطلوبة.

تطبيق الأجهزة الكهربائية لديه فقط عرض الأجهزة لعرضها في الوقت الحالي ؛ أقترح عليك إنشاء مكون للمصادقة (والذي سيبقى محاكيًا في الوقت الحالي) وستنشئ قائمة تسمح لك بالتنقل بين طرق العرض.

أولاً ، قم بإنشاء المكون مع CLI :

ng g c auth
سيتعين عليك أيضًا تعديل التنظيم الحالي قليلاً من أجل دمج التوجيه بسهولة أكبر: ستقوم بإنشاء مكون سيحتوي على كل طريق العرض الحالية والذي سيتم استدعاؤه AppareilViewComponent :

ng g c appareil-view
ثم قص محتويات العمود بالكامل app.component.html وحفظها في appareil-view.component.html واستبدالها بالعلامة الجديدة <app-appareil-view> :

<div class="container">
  <div class="row">
    <div class="col-xs-12">
      <app-appareil-view></app-appareil-view>
    </div>
  </div>
</div>
سيكون من الضروري أيضًا نقل منطق هذا العرض بحيث يعمل كل شيء مرة أخرى:   AppareilService إدخال وإنشاء الصفيف   appareils ودمج المنطق   ngOnInit  ونقل الوظائف   onAllumer()   و   onEteindre()  :

import { Component, OnInit } from '@angular/core';
import { AppareilService } from '../services/appareil.service';

@Component({
  selector: 'app-appareil-view',
  templateUrl: './appareil-view.component.html',
  styleUrls: ['./appareil-view.component.scss']
})
export class AppareilViewComponent implements OnInit {

  appareils: any[];

  lastUpdate = new Promise((resolve, reject) => {
    const date = new Date();
    setTimeout(
      () => {
        resolve(date);
      }, 2000
    );
  });

  constructor(private appareilService: AppareilService) { }

  ngOnInit() {
    this.appareils = this.appareilService.appareils;
  }

  onAllumer() {
    this.appareilService.switchOnAll();
  }

  onEteindre() {
    if(confirm('Etes-vous sûr de vouloir éteindre tous vos appareils ?')) {
      this.appareilService.switchOffAll();
    } else {
      return null;
    }
  }

}
يمكنك القيام بالتنظيف من   AppComponent خلال إزالة كل ما لم تعد هناك حاجة إليه. قم أيضًا بإنشاء قيمة منطقية   isAuth  في   AppareilViewComponent ، وقم بالإعلان عنها   false ، لأنك ستدمج خدمة مصادقة للبقية.

أضف شريط التنقل التالي إلى   AppComponent  :  

<nav class="navbar navbar-default">
  <div class="container-fluid">
    <div class="navbar-collapse">
      <ul class="nav navbar-nav">
        <li><a href="#">Authentification</a></li>
        <li class="active"><a href="#">Appareils</a></li>
      </ul>
    </div>
  </div>
</nav>
الآن كل شيء جاهز لإنشاء توجيه التطبيق.

إنشاء الطرق


بادئ ذي بدء ، ما هو الطريق او المسار في تطبيق Angular؟
هذه هي تعليمات العرض التي يجب اتباعها لكل عنوان URL ، أي المكون (المكونات) التي سيتم عرضها في أي موقع (مواقع) لعنوان URL معين.

نظرًا لأن توجيه التطبيق أمر أساسي لتشغيله ، فإننا نعلن المسارات في    app.module.ts  .

من الممكن أن يكون لديك ملف منفصل للتوجيه ، ولكن من حيث الوظائف ، لا يغير ذلك أي شيء: إنها مجرد مسألة تنظيم التعليمات البرمجية.
نقوم بإنشاء ثابت نوع   Routes  (الذي نستورده من   @angular/router  ) وهو مجموعة من كائنات JS التي تأخذ شكلًا معينًا:


import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { MonPremierComponent } from './mon-premier/mon-premier.component';
import { AppareilComponent } from './appareil/appareil.component';
import { FormsModule } from '@angular/forms';
import { AppareilService } from './services/appareil.service';
import { AuthComponent } from './auth/auth.component';
import { AppareilViewComponent } from './appareil-view/appareil-view.component';
import { Routes } from '@angular/router';

const appRoutes: Routes = [
  { path: 'appareils', component: AppareilViewComponent },
  { path: 'auth', component: AuthComponent },
  { path: '', component: AppareilViewComponent }
];
يتوافق المسار مع السلسلة التي ستأتي بعد   /  في عنوان URL : على الخادم المحلي ، لذلك يتوافق المسار الأول هنا    localhost:4200/appareils  .

لا تضف شرطة مائلة في بداية   path  .
بعد ذلك ،   component   يتوافق مع المكون الذي نريد عرضه عندما ينتقل المستخدم إلى العنصر   path  المختار.

أضفت   path  فراغًا ، والذي يتوافق ببساطة مع   localhost:4200  (أو جذر التطبيق وحده) ، لأنه إذا لم نعالج   path  الفراغ ، فإن كل تحديث للتطبيق سيتسبب في تعطله. سأوضح لك طرقًا أخرى للتعامل مع ذلك في الفصول التالية.
يتم الآن إنشاء المسارات ، ولكن يجب حفظها في تطبيقك. لذلك ، ستقوم بالاستيراد   RouterModule  من   @angular/router  وستقوم بإضافته إلى عمليات استيراد الصفيف الخاصة بك   AppModule ، بينما تطلق عليها الطريقة   forRoot()  بتمريرها صفيف المسارات التي أنشأتها للتو:

imports: [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot(appRoutes)
],
الآن بعد حفظ المسارات ، يبقى فقط إخبار Angular حيث تريد عرض المكونات في القالب عندما ينتقل المستخدم إلى المسار المعني. نستخدم العلامة <router-outlet> :

<div class="container">
  <div class="row">
    <div class="col-xs-12">
      <router-outlet></router-outlet>
    </div>
  </div>
</div>
عند تغيير المسار (في الوقت الحالي ، من خلال تعديل عنوان URL مباشرة في شريط عنوان المتصفح ) ، لا يتم إعادة تحميل الصفحة ، ولكن المحتوى الموجود أسفل شريط التنقل. في الفصل التالي ، ستقوم بدمج الروابط في شريط التنقل بحيث يمكن للمستخدم التنقل بسهولة.

انتقل مع routerLink


لكي يتمكن المستخدم من التنقل داخل التطبيق الخاص بك ، من الضروري إنشاء روابط أو أزرار تنقل إلى المسارات التي قمت بإنشائها. في الفصل السابق ، أنشأت روابط نموذجية في شريط التنقل ، ولكنها لا تفعل شيئًا في الوقت الحالي.

يمكنك أن تخبر نفسك أنه سيكون كافيًا وضع علامة على   path  مساراتك مباشرة في السمة   href ، ومن الناحية الفنية ، سيسمح لك هذا بالوصول إلى المسارات التي أنشأتها.
فلماذا لا نفعل ذلك على هذا النحو؟

ببساطة ، إذا نظرت عن كثب ، باستخدام هذه التقنية ، يتم إعادة تحميل الصفحة مع كل نقرة! نفقد تمامًا اهتمام تطبيق صفحة واحدة!
لذا ، نزيل السمة   href  ونستبدلها بالسمة   routerLink  :

<nav class="navbar navbar-default">
  <div class="container-fluid">
    <div class="navbar-collapse">
      <ul class="nav navbar-nav">
        <li><a routerLink="auth">Authentification</a></li>
        <li class="active"><a routerLink="appareils">Appareils</a></li>
      </ul>
    </div>
  </div>
</nav>
<div class="container">
  <div class="row">
    <div class="col-xs-12">
      <router-outlet></router-outlet>
    </div>
  </div>
</div>
وبالتالي ، يتم تحميل الطرق على الفور: يتم الحفاظ على بيئة عمل SPA.

لإنهاء هذه الخطوة ، سيكون من المثير للاهتمام إذا كانت الفئة   active  يطبق فقط على رابط المكون النشط حقًا. لحسن الحظ ، توفر Angular سمة لذلك يمكن إضافتها إلى الرابط مباشرة أو إلى العنصر الأصلي:

<ul class="nav navbar-nav">
    <li routerLinkActive="active"><a routerLink="auth">Authentification</a></li>
    <li routerLinkActive="active"><a routerLink="appareils">Appareils</a></li>
</ul>
الآن يتم تنشيط الروابط بصريا.

انتقل مع جهاز التوجيه Router


قد تكون هناك حالات تحتاج فيها إلى تنفيذ التعليمات البرمجية قبل التصفح. على سبيل المثال ، قد تحتاج إلى مصادقة مستخدم ، وإذا نجحت المصادقة ، فانتقل إلى الصفحة التي يريد المستخدم رؤيتها. أقترح أن تقوم بدمج هذه الوظيفة في تطبيق الأجهزة الكهربائية (ستبقى المصادقة نفسها محاكاة في الوقت الحالي).

أولا، إنشاء ملف جديد   auth.service.ts  في مجلد الخدمات لإدارة التوثيق (لا تنسى أن تضيف أيضا في مجموعة   providers  في   AppModule  ) :

export class AuthService {

  isAuth = false;

  signIn() {
    return new Promise(
      (resolve, reject) => {
        setTimeout(
          () => {
            this.isAuth = true;
            resolve(true);
          }, 2000
        );
      }
    );
  }

  signOut() {
    this.isAuth = false;
  }
}
 المتغير   isAuth  يعطي حالة مصادقة المستخدم . تقوم الطريقة    signOut()  "بفصل" المستخدم ، وتقوم الطريقة   signIn()  تلقائيًا بمصادقة المستخدم بعد ثانيتين ، مما يحاكي تأخير الاتصال بالخادم.

في المكون   AuthComponent ، ستقوم ببساطة بإنشاء زرين والطرق المقابلة للتوصيل والفصل (والتي سيتم عرضها في سياق سياقي: لن يتم عرض زر "الاتصال" إلا إذا تم قطع اتصال المستخدم والعكس صحيح) :

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../services/auth.service';

@Component({
  selector: 'app-auth',
  templateUrl: './auth.component.html',
  styleUrls: ['./auth.component.scss']
})
export class AuthComponent implements OnInit {

  authStatus: boolean;

  constructor(private authService: AuthService) { }

  ngOnInit() {
    this.authStatus = this.authService.isAuth;
  }

  onSignIn() {
    this.authService.signIn().then(
      () => {
        console.log('Sign in successful!');
        this.authStatus = this.authService.isAuth;
      }
    );
  }

  onSignOut() {
    this.authService.signOut();
    this.authStatus = this.authService.isAuth;
  }

}
نظرًا لأن أسلوب     الخدمة signIn() يعرض Promise ، يمكننا استخدام وظيفة رد اتصال غير متزامنة مع   .then()   تنفيذ التعليمات البرمجية بمجرد حل Promise . ما عليك سوى إضافة الأزرار ، وسيكون كل شيء جاهزًا لدمج التنقل:

<h2>Authentification</h2>
<button class="btn btn-success" *ngIf="!authStatus" (click)="onSignIn()">Se connecter</button>
<button class="btn btn-danger" *ngIf="authStatus" (click)="onSignOut()">Se déconnecter</button>
سيكون السلوك المطلوب أنه بمجرد مصادقة المستخدم ، ينتقل التطبيق تلقائيًا إلى عرض الجهاز. للقيام بذلك ، يجب عليك حقن   Router  (مستورد من   @angular/router  ) للوصول إلى الطريقة   navigate()  :

constructor(private authService: AuthService, private router: Router) { }
onSignIn() {
    this.authService.signIn().then(
      () => {
        console.log('Sign in successful!');
        this.authStatus = this.authService.isAuth;
        this.router.navigate(['appareils']);
      }
    );
}
تأخذ وظيفة التنقل كوسيطة مصفوفة من العناصر (مما يجعل من الممكن إنشاء مسارات من المتغيرات ، على سبيل المثال) والتي ، في هذه الحالة ، لديها عضو واحد فقط: العضو   path  المطلوب.
لا يزال المسار   appareils  متاحًا حتى الان ، حتى بدون مصادقة: في فصل لاحق ، ستتعلم كيفية تأمينه بالكامل. قبل ذلك ، ستتعلم كيفية إضافة مُدخلات إلى مساراتك.

إعدادات المسار


تخيل أنك تريد أن تكون قادرًا على النقر فوق جهاز في قائمة الأجهزة لعرض صفحة تحتوي على مزيد من المعلومات حول هذا الجهاز: يمكنك تخيل نظام توجيه نوع   appareils/nom-de-l'appareil ، على سبيل المثال. إذا كان لدينا جهازان أو ثلاثة فقط ، فقد نميل إلى إنشاء مسار لكل جهاز ، ولكن تخيل حالة لدينا 30 جهازًا أو 300 جهاز. تخيل أننا سمحنا للمستخدم بإنشاء أجهزة جديدة ؛ نهج إنشاء طريق عن طريق الجهاز غير مناسب. في هذا النوع من الحالات ، نفضل اختيار إنشاء مسار مع مُدخلات.

أولاً ، ستنشئ المسار في   AppModule  :

const appRoutes: Routes = [
  { path: 'appareils', component: AppareilViewComponent },
  { path: 'appareils/:id', component: SingleAppareilComponent },
  { path: 'auth', component: AuthComponent },
  { path: '', component: AppareilViewComponent }
];
باستخدام النقطتين : قبل أن يعلن جزء المسار عن هذا الجزء كمُدخل: appareils/* سيتم إرجاع جميع مسارات الكتابة SingleAppareilComponent ، والتي ستنشئها الآن:

import { Component, OnInit } from '@angular/core';
import { AppareilService } from '../services/appareil.service';

@Component({
  selector: 'app-single-appareil',
  templateUrl: './single-appareil.component.html',
  styleUrls: ['./single-appareil.component.scss']
})
export class SingleAppareilComponent implements OnInit {

  name: string = 'Appareil';
  status: string = 'Statut';

  constructor(private appareilService: AppareilService) { }

  ngOnInit() {
  }

}

<h2>{ { name }}</h2>
<p>Statut : { { status }}</p>
<a routerLink="/appareils">Retour à la liste</a>
في الوقت الحالي ، إذا انتقلت إلى   /appareils/nom أي اسم تختاره ، فيمكنك الوصول إليه   SingleAppareilComponent  . الآن ، ستدخل هناك   ActivatedRoute ، مستورد من   @angular/router ، لاستعادة جزء   id  من عنوان URL :

constructor(private appareilService: AppareilService,
            private route: ActivatedRoute) { }
بعد ذلك ، في ngOnInit() ، ستستخدم الكائن snapshot الذي يحتوي على مُدخلات عنوان URL ، وفي الوقت الحالي ، قم بتعيين المُدخل id للمتغير name :

ngOnInit() {
    this.name = this.route.snapshot.params['id'];
}
لذلك ، appareils/   سيظهر الجزء الذي تكتبه في شريط العناوين بعد ذلك   في القالب ، ولكن هذا ليس هو السلوك الذي تريده. لتحقيق الهدف المنشود ، ابدأ بإضافة   AppareilService معرّف فريد لكل جهاز وطريقة تجعل الجهاز يتوافق مع المعرف:

appareils = [
    {
      id: 1,
      name: 'Machine à laver',
      status: 'éteint'
    },
    {
      id: 2,
      name: 'Frigo',
      status: 'allumé'
    },
    {
      id: 3,
      name: 'Ordinateur',
      status: 'éteint'
    }
];
getAppareilById(id: number) {
    const appareil = this.appareils.find(
      (s) => {
        return s.id === id;
      }
    );
    return appareil;
}
الآن في SingleAppareilComponent ، ستقوم باسترداد معرف URL واستخدامه لاسترداد الجهاز المطابق:

ngOnInit() {
    const id = this.route.snapshot.params['id'];
    this.name = this.appareilService.getAppareilById(+id).name;
    this.status = this.appareilService.getAppareilById(+id).status;
}
نظرًا لأن جزءًا من عنوان URL هو بالضرورة من النوع    string  ، وأن الطريقة   getAppareilById()   تأخذ رقمًا كوسيطة ، يجب ألا ننسى استخدامه من   +  قبل   id  في الاستدعاء لتصريف المتغير كرقم.
يمكنك الانتقال يدويًا إلى   /appareils/2 ، على سبيل المثال ، لكنها لا تزال تعيد تحميل الصفحة ، وستفقد حالة الأجهزة (إذا قمت بتشغيلها أو إيقاف تشغيلها ، على سبيل المثال). لوضع اللمسات الأخيرة هذه الميزة، وتشمل المعرف الفريد في   AppareilComponent  و   AppareilViewComponent ثم إنشاء   routerLink  لكل جهاز يسمح للنظر في التفاصيل: 

@Input() appareilName: string;
@Input() appareilStatus: string;
@Input() index: number;
@Input() id: number;
<ul class="list-group">
  <app-appareil  *ngFor="let appareil of appareils; let i = index"
                 [appareilName]="appareil.name"
                 [appareilStatus]="appareil.status"
                 [index]="i" 
                 [id]="appareil.id"></app-appareil>
</ul>
<h4 [ngStyle]="{color: getColor()}">Appareil : { { appareilName }} -- Statut : { { getStatus() }}</h4>
<a [routerLink]="[id]">Détail</a>
هنا ، يمكنك استخدام صفيف التنسيق   routerLink  لربط الخاصية من أجل الوصول إلى المتغير   id  .
كفى ! يمكنك الآن الوصول إلى صفحة التفاصيل لكل جهاز ، ويتم تحديث معلومات الحالة هناك تلقائيًا من خلال استخدام الخدمة.

إعادة التوجيه Redirection


قد تكون هناك حالات نود فيها إعادة توجيه مستخدم ، على سبيل المثال لعرض صفحة 404 عندما يدخل عنوان URL غير موجود.

لتطبيق الأجهزة الكهربائية ، ابدأ بإنشاء مكون بسيط للغاية 404 ، يسمى   four-oh-four.component.ts  :

<h2>Erreur 404</h2>
<p>La page que vous cherchez n'existe pas !</p>
بعد ذلك ، ستضيف المسار "المباشر" إلى هذه الصفحة ، بالإضافة إلى مسار "حرف البدل" ، الذي سيعيد توجيه أي مسار غير معروف إلى صفحة الخطأ:

const appRoutes: Routes = [
  { path: 'appareils', component: AppareilViewComponent },
  { path: 'appareils/:id', component: SingleAppareilComponent },
  { path: 'auth', component: AuthComponent },
  { path: '', component: AppareilViewComponent },
  { path: 'not-found', component: FourOhFourComponent },
  { path: '**', redirectTo: 'not-found' }
];
لذلك عند إدخال مسار في شريط التنقل لا يدعمه التطبيق مباشرة ، تتم إعادة توجيهك إلى   /not-found 404  وبالتالي المكون .

Guards


قد تكون هناك حالات تريد فيها تشغيل التعليمات البرمجية قبل أن يتمكن المستخدم من الوصول إلى مسار ؛ على سبيل المثال ، قد ترغب في التحقق من مصادقتها أو هويتها. من الناحية الفنية ، سيكون هذا ممكنًا عن طريق إدخال رمز في طريقة   ngOnInit()  كل مكون ، لكنه سيصبح مرهقًا ، مع كود متكرر ومضاعفة الأخطاء المحتملة. لذلك سيكون من الأفضل أن يكون لديك طريقة لمركز هذا النوع من الوظائف. لهذا ، هناك guard (حارس)   canActivate  .
الحارس هو بالفعل خدمة ستقوم Angular بتنفيذها عندما يحاول المستخدم التنقل إلى المسار المحدد. تقوم هذه الخدمة بتطبيق الواجهة   canActivate ، وبالتالي يجب أن تحتوي على طريقة تحمل نفس الاسم تأخذ الوسيطات   ActivatedRouteSnapshot  و   RouterStateSnapshot  (والتي سيتم توفيرها بواسطة Angular في وقت التشغيل) وترجع قيمة منطقية ، إما بشكل متزامن (منطقي) ، إما بشكل غير متزامن (في شكل Promise (وعد) أو ملحوظة):

import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';

export class AuthGuard implements CanActivate {
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    
  }
}
لا تنس استيراد العناصر المختلفة في أعلى الملف.

إذا كنت لا تعرف برنامج Observables حتى الآن ، فلا تقلق ، فستجدها بالتفصيل في الفصل التالي.
احفظ هذا الملف في المجلد   services  تحت الاسم   auth-guard.service.ts  .

ثم عليك إدخال الخدمة   AuthService  في هذه الخدمة الجديدة. لحقن خدمة في خدمة أخرى ، يجب أن يكون للخدمة التي تحقن فيها آخر   @Injectable ، للاستيراد من   @angular/core  : 

import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { AuthService } from './auth.service';
import { Injectable } from '@angular/core';

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private authService: AuthService) { }

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {

  }
}
داخل الطريقة   canActivate() ، سوف تتحقق من حالة المصادقة   AuthService  . إذا تمت مصادقة المستخدم ، فستعيد الطريقة   true ، مما يسمح بالوصول إلى المسار المحمي. خلاف ذلك ، يمكن اعادة   false ، ولكنه سيمنع ببساطة الوصول دون مزيد من الوظائف. سيكون من المثير للاهتمام إعادة توجيه المستخدم إلى صفحة المصادقة ، ودفعه إلى تعريف نفسه:

import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { AuthService } from './auth.service';
import { Injectable } from '@angular/core';

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private authService: AuthService,
              private router: Router) { }

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    if(this.authService.isAuth) {
      return true;
    } else {
      this.router.navigate(['/auth']);
    }
  }
}
لتطبيق هذا الحارس على الطريق /appareils وعلى جميع مساراته الفرعية ، يجب إضافته إلى AppModule . لا تنس أن تضيف AuthGuard إلى المصفوفة providers ، لأنها خدمة:

const appRoutes: Routes = [
  { path: 'appareils', canActivate: [AuthGuard], component: AppareilViewComponent },
  { path: 'appareils/:id', canActivate: [AuthGuard], component: SingleAppareilComponent },
  { path: 'auth', component: AuthComponent },
  { path: '', component: AppareilViewComponent },
  { path: 'not-found', component: FourOhFourComponent },
  { path: '**', redirectTo: 'not-found' }
];
الآن ، إذا حاولت الوصول   /appareils  دون مصادقة ، فسيتم إعادة توجيهك تلقائيًا إلى   /auth  . إذا قمت بالنقر فوق "اتصال" ، يمكنك الوصول إلى قائمة الأجهزة دون أي مشكلة.
نظرًا لأن المسار   /appareils  محمي ، يمكنك إزالة الروابط   disabled  من الزرين "تشغيل الكل" و "إيقاف تشغيل الكل" ، لأنه يمكنك التأكد من أن أي مستخدم يصل إلى هذا المسار سيتم مصادقته.

في هذا الفصل ، تعلمت إدارة توجيه تطبيقك   routerLink  وبرمجياً ( مع   router.navigate()  ) . لقد رأيت أيضًا كيفية إعادة توجيه المستخدم ، وكيفية حماية المسارات في تطبيقك باستخدام الحراس guards .