Angular 2 التمرير إلى الأعلى عند تغيير المسار: لماذا هو مهم وكيفية تنفيذه
\n\nتخيل أنك تتصفح صفحة طويلة من الوثائق، وتصل إلى المنتصف، ثم تنقر على رابط - وتجد نفسك في أسفل قسم جديد. هل هذا مألوف؟ هذه هي المشكلة التي يحلها ميكانيك Angular 2 التمرير إلى الأعلى عند تغيير المسار. بدونها، تتأثر تجربة المستخدم: يضطر المستخدم إلى التمرير يدويًا باستخدام عجلة الفأرة أو الضغط على زر Home، مما يسبب الإزعاج ويزيد من معدل الارتداد.
\n\nفي هذه المقالة، سنشرح كيفية جعل Angular يقوم تلقائيًا بتمرير الصفحة إلى الأعلى عند كل انتقال بين المسارات. ستتعلم ليس فقط الحل الجاهز، بل ستفهم أيضًا لماذا يعمل بهذه الطريقة. المقالة مكتوبة للمبتدئين، ولكنها ستكون مفيدة أيضًا للمطورين ذوي الخبرة الذين يرغبون في تحديث معرفتهم.
\n\nلماذا لا يقوم Angular بذلك "افتراضيًا"؟
\n\nAngular هو إطار عمل قوي لتطبيقات الصفحة الواحدة (SPA)، ولكن سلوكه أثناء التنقل قد يكون مفاجئًا. افتراضيًا، عند تغيير المسار، لا يعيد المتصفح تعيين موضع التمرير. لماذا؟ لأن Angular Router يعتبر الانتقال بمثابة استبدال المكون في <router-outlet>، وليس تحميل صفحة جديدة. بينما يعتقد المتصفح: "ما زلت في نفس المستند، لماذا يجب أن أتمرر؟".
هذا ملحوظ بشكل خاص إذا كان لديك قوائم طويلة أو جداول أو وثائق. ينقر المستخدم على رابط في أسفل الصفحة - ويتم فتح المحتوى الجديد، لكن التمرير يبقى في الأسفل. حل هذه المشكلة هو إخبار Angular بوضوح: "مرحبًا، عند كل انتقال، قم بالتمرير إلى الأعلى!".
\n\nطريقتان للحل
\n\nهناك طريقتان رئيسيتان لتنفيذ التمرير إلى الأعلى عند تغيير المسار في Angular 2+:
\n\n- \n
- معالج عام في AppComponent - بسيط، لكنه لا يعمل دائمًا بشكل صحيح مع المسارات المتداخلة. \n
- استخدام تكوين المسار (Route Config) - أكثر مرونة، ويسمح بتخصيص السلوك لكل مسار على حدة. \n
سننظر في كليهما، لكنني أوصي بالتركيز على الثاني - فهو يمنحك تحكمًا أكبر.
\n\nالطريقة 1: معالج عام بسيط (لبداية سريعة)
\n\nافتح ملف app.component.ts (أو المكون الجذري) وأضف اشتراكًا في أحداث Router. الكود بلغة TypeScript:
\nimport { Component, OnInit } from '@angular/core';\nimport { Router, NavigationEnd } from '@angular/router';\nimport { filter } from 'rxjs/operators';\n\n@Component({\n selector: 'app-root',\n templateUrl: './app.component.html',\n styleUrls: ['./app.component.css']\n})\nexport class AppComponent implements OnInit {\n constructor(private router: Router) {}\n\n ngOnInit() {\n this.router.events.pipe(\n filter(event => event instanceof NavigationEnd)\n ).subscribe(() => {\n window.scrollTo(0, 0);\n });\n }\n}\n\n\nهنا، نشترك في أحداث الموجه، ونقوم بتصفية NavigationEnd فقط (لحظة اكتمال الانتقال)، ثم نستدعي window.scrollTo(0, 0). الأمر بسيط، لكن هناك فارق بسيط: إذا كان لديك مسارات متداخلة (مثل مسارات فرعية داخل مكون)، فقد يتم تشغيل هذا الكود مبكرًا جدًا أو لا يتم تشغيله على الإطلاق.
الطريقة 2: الحل الصحيح باستخدام تكوين المسار (موصى به)
\n\nيستخدم هذا الأسلوب الخيار المدمج scrollPositionRestoration في Angular Router. بدءًا من Angular 6.1، أصبح من الممكن تكوين الاستعادة/إعادة التعيين التلقائي للتمرير مباشرة في تكوين التوجيه.
افتح ملف app-routing.module.ts وأضف الخيار scrollPositionRestoration: 'top' في RouterModule.forRoot():
\nimport { NgModule } from '@angular/core';\nimport { RouterModule, Routes } from '@angular/router';\n\nconst routes: Routes = [\n { path: '', component: HomeComponent },\n { path: 'about', component: AboutComponent },\n { path: 'contact', component: ContactComponent }\n];\n\n@NgMod