No encontré una solución estándar y las que se ofrecen en Internet están limitadas a una página. Por lo tanto, escribí el mío y decidí compartirlo.
Descripción del caso
Las páginas de la aplicación se dividen en 3 grupos:
- Solo para usuarios autorizados
- Solo para usuarios no autorizados
- Para cualquier usuario
Puede iniciar o cerrar sesión en cualquier página.
Si ingresa / sale en una página con acceso limitado, entonces debe ir a la página permitida.
Si la página no tiene restricciones, debe permanecer en la página actual.
Para una mejor comprensión, es recomendable conocer:
Decisión
La diferenciación de derechos de acceso a las páginas se realiza a través de guard - CanActivate. La verificación se realiza al cambiar a la página, pero no reacciona a los cambios de derechos cuando la transición ya se ha realizado. Para evitar la duplicación de la lógica para los derechos de acceso, reiniciamos los guardias a la fuerza.
El reinicio de los guardias de la página actual se realiza navegando por la URL actual. Y cambios en las estrategias Router.onSameUrlNavigation y Route.runGuardsAndResolvers.
Aquí tienes una solución lista para usar. Más detalles en la siguiente sección.
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];
}
});
}
}
}
Descripción adicional de la solución
Lo primero que debe intentar para reiniciar los guardias es usar la navegación a la URL actual.
this.router.navigateByUrl(this.router.url);
Pero, de forma predeterminada, el evento de transición de URL actual se ignora y no sucede nada. Para que esto funcione, debe configurar el enrutamiento.
Configurar el enrutamiento
1. Cambie la estrategia del enrutador. onSameUrlNavigation
onSameUrlNavigation puede tomar los siguientes valores:
onSameUrlNavigation: 'reload' | 'ignore';
Para la sensibilidad a la transición a la URL actual, debe configurar 'recargar'.
El cambio de política no se vuelve a cargar, pero genera un evento de navegación adicional. Se puede obtener mediante suscripción:
this.router.events.subscribe();
2. Cambie la estrategia de ruta. runGuardsAndResolvers
runGuardsAndResolvers puede tomar los siguientes valores:
type RunGuardsAndResolvers = 'pathParamsChange' | 'pathParamsOrQueryParamsChange' | 'paramsChange' | 'paramsOrQueryParamsChange' | 'always' | ((from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot) => boolean);
Para la sensibilidad a la transición a la URL actual, debe establecer "siempre".
Configurar el enrutamiento durante la configuración de la aplicación
onSameUrlNavigation:
const routes: : Route[] = [];
@NgModule({
imports: [
RouterModule.forRoot(
routes,
{ onSameUrlNavigation: 'reload' }
)
]
})
runGuardsAndResolvers:
const routes: Route[] = [
{
path: '',
component: AppComponent,
runGuardsAndResolvers: 'always',
}
];
Configurar el enrutamiento en tiempo de ejecución
constructor(
private router: Router,
private route: ActivatedRoute
) {
this.router.onSameUrlNavigation = 'reload';
this.route.routeConfig.runGuardsAndResolvers = 'always';
}
Reinicio de guardias
Para reiniciar los guardias de una página específica, es suficiente configurar el enrutamiento durante la configuración.
Pero para recargar los guardias de cualquier página, cambiar runGuardsAndResolvers en cada ruta dará lugar a comprobaciones innecesarias. Y la necesidad de recordar siempre este parámetro, a los errores.
Dado que nuestro caso asume un reinicio para cualquier página sin restricciones en la configuración de la aplicación, necesita:
1. Reemplace onSameUrlNavigation y mantenga el valor actual
// 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. Obtenga ActivatedRoute para la URL actual
Dado que la inyección ActivatedRoute se lleva a cabo en el servicio, la ActivatedRoute resultante no está asociada con la URL actual.
ActivatedRoute para la URL actual se encuentra en la última salida principal y debe encontrarla:
// 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. Reemplace runGuardsAndResolvers por todo ActivatedRoute y sus ancestros, manteniendo los valores actuales
Un guardia que restringe el acceso puede ubicarse en cualquiera de los antepasados de la ActivatedRoute actual. Todos los antepasados se encuentran en 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. Ir a la URL actual
this.router.navigateByUrl(this.router.url);
5. Devuelva runGuardsAndResolvers y onSameUrlNavigation a su estado original
restoreRunGuards();
restoreSameUrl();
6. Combinar etapas en una función
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();
});
}
Espero que este artículo te haya sido útil. Si hay otras soluciones, estaré encantado de verlas en los comentarios.