Não encontrei uma solução padrão e as oferecidas na Internet limitam-se a uma página. Portanto, escrevi o meu próprio e decidi compartilhá-lo.
Descrição do caso
As páginas do aplicativo são divididas em 3 grupos:
- Apenas para usuários autorizados
- Apenas para usuários não autorizados
- Para qualquer usuário
Você pode fazer login ou logout em qualquer página.
Se você entrar / sair de uma página com acesso limitado, precisará ir para a página permitida.
Se a página não tiver restrições, você precisará permanecer na página atual.
Para um melhor entendimento, é aconselhável conhecer:
Decisão
A diferenciação dos direitos de acesso às páginas é realizada através do guard - CanActivate. A verificação é realizada quando você vai para a página, mas não reage às mudanças nos direitos quando a transição já foi feita. Para evitar a duplicação da lógica dos direitos de acesso, reiniciamos os guardas à força.
O reinício dos guardas para a página atual é feito navegando no url atual. E mudanças nas estratégias Router.onSameUrlNavigation e Route.runGuardsAndResolvers.
Aqui está uma solução pronta. Mais detalhes na próxima seção.
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];
}
});
}
}
}
Descrição adicional da solução
A primeira coisa que você deseja tentar reiniciar os guardas é usar a navegação para a url atual.
this.router.navigateByUrl(this.router.url);
Mas, por padrão, o evento de transição de url atual é ignorado e nada acontece. Para que isso funcione, você precisa configurar o roteamento.
Configurando o roteamento
1. Mude a estratégia do roteador. onSameUrlNavigation
onSameUrlNavigation pode assumir os seguintes valores:
onSameUrlNavigation: 'reload' | 'ignore';
Para sensibilidade à transição para o url atual, você precisa definir 'recarregar'.
A mudança de política não recarrega, mas gera um evento de navegação adicional. Pode ser obtido por meio de uma assinatura:
this.router.events.subscribe();
2. Altere a estratégia de rota. runGuardsAndResolvers
runGuardsAndResolvers pode assumir os seguintes valores:
type RunGuardsAndResolvers = 'pathParamsChange' | 'pathParamsOrQueryParamsChange' | 'paramsChange' | 'paramsOrQueryParamsChange' | 'always' | ((from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot) => boolean);
Para sensibilidade à transição para o url atual, você precisa definir 'sempre'.
Configurando o roteamento durante a configuração do aplicativo
onSameUrlNavigation:
const routes: : Route[] = [];
@NgModule({
imports: [
RouterModule.forRoot(
routes,
{ onSameUrlNavigation: 'reload' }
)
]
})
runGuardsAndResolvers:
const routes: Route[] = [
{
path: '',
component: AppComponent,
runGuardsAndResolvers: 'always',
}
];
Configurando o roteamento em tempo de execução
constructor(
private router: Router,
private route: ActivatedRoute
) {
this.router.onSameUrlNavigation = 'reload';
this.route.routeConfig.runGuardsAndResolvers = 'always';
}
Reiniciando guardas
Para reiniciar os guardas de uma página específica, basta configurar o roteamento durante a configuração.
Mas para recarregar os guardas de qualquer página, alterar runGuardsAndResolvers em cada rota levará a verificações desnecessárias. E a necessidade de sempre lembrar este parâmetro - para erros.
Como nosso caso pressupõe a reinicialização de qualquer página sem restrições na configuração do aplicativo, você precisa de:
1. Substitua onSameUrlNavigation e mantenha o valor atual
// 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. Obtenha ActivatedRoute para o url atual
Como o ActivatedRoute injetado é executado no serviço, o ActivatedRoute resultante não está associado ao url atual.
ActivatedRoute para o url atual está na última saída primária e você precisa encontrá-lo:
// 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. Substitua runGuardsAndResolvers por todos os ActivatedRoute e seus ancestrais, mantendo os valores atuais
Um guarda que restringe o acesso pode ser localizado em qualquer um dos ancestrais do ActivatedRoute atual. Todos os ancestrais estão localizados em 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. Vá para o url atual
this.router.navigateByUrl(this.router.url);
5. Retorne runGuardsAndResolvers e onSameUrlNavigation ao seu estado original
restoreRunGuards();
restoreSameUrl();
6. Combine estágios em uma função
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 artigo tenha sido útil para você. Se houver outras soluções, ficarei feliz em vê-las nos comentários.