لم أجد حلاً قياسيًا ، وتلك المعروضة على الإنترنت تقتصر على صفحة واحدة. لذلك ، كتبت رسالتي الخاصة وقررت مشاركتها.
وصف حالة
صفحات التطبيق مقسمة إلى 3 مجموعات:
- للمستخدمين المصرح لهم فقط
- للمستخدمين غير المصرح لهم فقط
- لأي مستخدمين
يمكنك تسجيل الدخول أو تسجيل الخروج من أي صفحة.
إذا قمت بالدخول / الخروج من صفحة ذات وصول محدود ، فأنت بحاجة إلى الانتقال إلى الصفحة المسموح بها.
إذا كانت الصفحة لا تحتوي على قيود ، فأنت بحاجة إلى البقاء في الصفحة الحالية.
لفهم أفضل ، يُنصح بمعرفة:
القرار
يتم تنفيذ التفريق بين حقوق الوصول إلى الصفحات من خلال guard - CanActivate. يتم إجراء الفحص عند الانتقال إلى الصفحة ، ولكنه لا يتفاعل مع التغييرات في الحقوق عندما يكون الانتقال قد تم بالفعل. لتجنب تكرار منطق حقوق الوصول ، نقوم بإعادة تشغيل الحراس بالقوة.
تتم إعادة تشغيل أدوات الحماية للصفحة الحالية من خلال التنقل في عنوان url الحالي. وتغييرات في الإستراتيجيات Router.onSameUrlNavigation و Route.runGuardsAndResolvers.
إليك حل جاهز. مزيد من التفاصيل في القسم التالي.
import { Injectable } from '@angular/core';
import { ActivatedRoute, PRIMARY_OUTLET, Router, RunGuardsAndResolvers } from '@angular/router';
@Injectable()
export class GuardControlService {
constructor(
private route: ActivatedRoute,
private router: Router,
) {}
/**
* guard- url
*/
forceRunCurrentGuards(): void {
// Router.onSameUrlNavigation url
const restoreSameUrl = this.changeSameUrlStrategy(this.router, 'reload');
// ActivatedRoute primary outlet
const primaryRoute: ActivatedRoute = this.getLastRouteForOutlet(this.route.root, PRIMARY_OUTLET);
// runGuardsAndResolvers ActivatedRoute url
const restoreRunGuards = this.changeRunGuardStrategies(primaryRoute, 'always');
//
this.router.navigateByUrl(
this.router.url
).then(() => {
// onSameUrlNavigation
restoreSameUrl();
// runGuardsAndResolvers
restoreRunGuards();
});
}
/**
* onSameUrlNavigation
* @param router - Router,
* @param strategy -
* @return callback
*/
private changeSameUrlStrategy(router: Router, strategy: 'reload' | 'ignore'): () => void {
const onSameUrlNavigation = router.onSameUrlNavigation;
router.onSameUrlNavigation = strategy;
return () => {
router.onSameUrlNavigation = onSameUrlNavigation;
}
}
/**
* route outlet-
* @param route - Route
* @param outlet - outlet-,
* @return ActivatedRoute outlet
*/
private getLastRouteForOutlet(route: ActivatedRoute, outlet: string): ActivatedRoute {
if (route.children?.length) {
return this.getLastRouteForOutlet(
route.children.find(item => item.outlet === outlet),
outlet
);
} else {
return route;
}
}
/**
* runGuardsAndResolvers ActivatedRoute ,
* @param route - ActivatedRoute
* @param strategy -
* @return callback
*/
private changeRunGuardStrategies(route: ActivatedRoute, strategy: RunGuardsAndResolvers): () => void {
const routeConfigs = route.pathFromRoot
.map(item => {
if (item.routeConfig) {
const runGuardsAndResolvers = item.routeConfig.runGuardsAndResolvers;
item.routeConfig.runGuardsAndResolvers = strategy;
return runGuardsAndResolvers;
} else {
return null;
}
});
return () => {
route.pathFromRoot
.forEach((item, index) => {
if (item.routeConfig) {
item.routeConfig.runGuardsAndResolvers = routeConfigs[index];
}
});
}
}
}
وصف الحل الإضافي
أول شيء تريد محاولة إعادة تشغيل الحراس هو استخدام التنقل إلى عنوان url الحالي.
this.router.navigateByUrl(this.router.url);
ولكن ، بشكل افتراضي ، يتم تجاهل حدث انتقال عنوان url الحالي ولا يحدث شيء. لكي يعمل هذا ، تحتاج إلى تكوين التوجيه.
إعداد التوجيه
1. قم بتغيير استراتيجية جهاز التوجيه. onSameUrlNavigation
يمكن أن يأخذ onSameUrlNavigation القيم التالية:
onSameUrlNavigation: 'reload' | 'ignore';
من أجل الحساسية للانتقال إلى عنوان url الحالي ، تحتاج إلى تعيين "إعادة التحميل".
لا يتم إعادة تحميل تغيير السياسة ، ولكنه ينشئ حدث تنقل إضافي. يمكن الحصول عليها من خلال الاشتراك:
this.router.events.subscribe();
2. تغيير استراتيجية الطريق. runGuardsAndResolvers
يمكن أن تأخذ runGuardsAndResolvers القيم التالية:
type RunGuardsAndResolvers = 'pathParamsChange' | 'pathParamsOrQueryParamsChange' | 'paramsChange' | 'paramsOrQueryParamsChange' | 'always' | ((from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot) => boolean);
من أجل الحساسية للانتقال إلى عنوان url الحالي ، تحتاج إلى تعيين "دائمًا".
تكوين التوجيه أثناء تكوين التطبيق
onSameUrl الملاحة:
const routes: : Route[] = [];
@NgModule({
imports: [
RouterModule.forRoot(
routes,
{ onSameUrlNavigation: 'reload' }
)
]
})
runGuardsAndResolvers:
const routes: Route[] = [
{
path: '',
component: AppComponent,
runGuardsAndResolvers: 'always',
}
];
تكوين التوجيه في وقت التشغيل
constructor(
private router: Router,
private route: ActivatedRoute
) {
this.router.onSameUrlNavigation = 'reload';
this.route.routeConfig.runGuardsAndResolvers = 'always';
}
إعادة تشغيل الحراس
لإعادة تشغيل أدوات الحماية لصفحة معينة ، يكفي تكوين التوجيه أثناء التكوين.
ولكن لإعادة تحميل أدوات الحماية لأي صفحة ، سيؤدي تغيير runGuardsAndResolvers في كل مسار إلى عمليات فحص غير ضرورية. والحاجة إلى تذكر هذه المعلمة دائمًا - للأخطاء.
نظرًا لأن حالتنا تفترض إعادة تشغيل أي صفحة دون قيود في إعداد التطبيق ، فأنت بحاجة إلى:
1. استبدل onSameUrlNavigation واحتفظ بالقيمة الحالية
// Router.onSameUrlNavigation url
const restoreSameUrl = this.changeSameUrlStrategy(this.router, 'reload');
...
/**
* onSameUrlNavigation
* @param router - Router,
* @param strategy -
* @return callback
*/
private changeSameUrlStrategy(router: Router, strategy: 'reload' | 'ignore'): () => void {
const onSameUrlNavigation = router.onSameUrlNavigation;
router.onSameUrlNavigation = strategy;
return () => {
router.onSameUrlNavigation = onSameUrlNavigation;
}
}
2. احصل على ActivatedRoute لعنوان url الحالي
نظرًا لأنه يتم تنفيذ ActivatedRoute للحقن في الخدمة ، فإن ActivatedRoute الناتجة غير مرتبطة بعنوان url الحالي.
ActivatedRoute لعنوان url الحالي يقع في آخر منفذ أساسي وتحتاج إلى العثور عليه:
// ActivatedRoute primary outlet
const primaryRoute: ActivatedRoute = this.getLastRouteForOutlet(this.route.root, PRIMARY_OUTLET);
...
/**
* route outlet-
* @param route - Route
* @param outlet - outlet-,
* @return ActivatedRoute outlet
*/
private getLastRouteForOutlet(route: ActivatedRoute, outlet: string): ActivatedRoute {
if (route.children?.length) {
return this.getLastRouteForOutlet(
route.children.find(item => item.outlet === outlet),
outlet
);
} else {
return route;
}
}
3. استبدل runGuardsAndResolvers لجميع ActivatedRoute وأسلافها ، مع الاحتفاظ بالقيم الحالية
يمكن وضع حارس يقيد الوصول في أي من أسلاف ActivatedRoute الحالية. تقع جميع الأجداد في pathFromRoot.
// runGuardsAndResolvers ActivatedRoute url
const restoreRunGuards = this.changeRunGuardStrategies(primaryRoute, 'always');
...
/**
* runGuardsAndResolvers ActivatedRoute ,
* @param route - ActivatedRoute
* @param strategy -
* @return callback
*/
private changeRunGuardStrategies(route: ActivatedRoute, strategy: RunGuardsAndResolvers): () => void {
const routeConfigs = route.pathFromRoot
.map(item => {
if (item.routeConfig) {
const runGuardsAndResolvers = item.routeConfig.runGuardsAndResolvers;
item.routeConfig.runGuardsAndResolvers = strategy;
return runGuardsAndResolvers;
} else {
return null;
}
});
return () => {
route.pathFromRoot
.forEach((item, index) => {
if (item.routeConfig) {
item.routeConfig.runGuardsAndResolvers = routeConfigs[index];
}
});
}
}
4. انتقل إلى عنوان url الحالي
this.router.navigateByUrl(this.router.url);
5. أعد runGuardsAndResolvers و onSameUrlNavigation إلى حالتهما الأصلية
restoreRunGuards();
restoreSameUrl();
6. الجمع بين المراحل في وظيفة واحدة
constructor(
private route: ActivatedRoute,
private router: Router,
) {}
/**
* guard- url
*/
forceRunCurrentGuards(): void {
// Router.onSameUrlNavigation url
const restoreSameUrl = this.changeSameUrlStrategy(this.router, 'reload');
// ActivatedRoute primary outlet
const primaryRoute: ActivatedRoute = this.getLastRouteForOutlet(this.route.root, PRIMARY_OUTLET);
// runGuardsAndResolvers ActivatedRoute url
const restoreRunGuards = this.changeRunGuardStrategies(primaryRoute, 'always');
//
this.router.navigateByUrl(
this.router.url
).then(() => {
// onSameUrlNavigation
restoreSameUrl();
// runGuardsAndResolvers
restoreRunGuards();
});
}
آمل أن تكون هذه المقالة مفيدة لك. إذا كانت هناك حلول أخرى ، فسأكون سعيدًا برؤيتها في التعليقات.