Angular 9. Memulai kembali pelindung halaman saat ini. Picu penjaga rute saat ini

Menghadapi kebutuhan untuk me-restart penjaga untuk halaman saat ini, terlepas dari halaman mana yang terbuka.



Saya tidak menemukan solusi standar, dan yang ditawarkan di Internet terbatas pada satu halaman. Oleh karena itu, saya menulis sendiri dan memutuskan untuk membagikannya.



Deskripsi kasus



Halaman aplikasi dibagi menjadi 3 kelompok:



  • Hanya untuk pengguna resmi

  • Hanya untuk pengguna yang tidak sah

  • Untuk semua pengguna



Anda dapat masuk atau keluar di halaman mana pun.



Jika Anda masuk / keluar pada halaman dengan akses terbatas, maka Anda harus pergi ke halaman yang diizinkan.



Jika halaman tersebut tidak memiliki batasan, Anda harus tetap berada di halaman saat ini.



Untuk pemahaman yang lebih baik, disarankan untuk mengetahui tentang:



  • Guard - CanActivate

  • Rute

  • Router

  • ActivatedRoute

  • Outlet router



Keputusan



Diferensiasi hak akses ke halaman dilakukan melalui penjaga - CanActivate. Pemeriksaan dilakukan saat Anda membuka halaman, tetapi tidak bereaksi terhadap perubahan hak saat transisi telah dilakukan. Untuk menghindari duplikasi logika untuk hak akses, kami secara paksa memulai ulang penjaga.



Restart penjaga untuk halaman saat ini dilakukan dengan menavigasi url saat ini. Dan perubahan pada strategi Router.onSameUrlNavigation dan Route.runGuardsAndResolvers.



Berikut solusi yang sudah jadi. Lebih detail di bagian selanjutnya.
  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];
            }
          });
      }
    }
  }
  




Deskripsi solusi tambahan



Hal pertama yang ingin Anda coba untuk memulai ulang penjaga adalah menggunakan navigasi ke url saat ini.



this.router.navigateByUrl(this.router.url);


Namun, secara default, peristiwa transisi url saat ini diabaikan dan tidak ada yang terjadi. Agar ini berfungsi, Anda perlu mengkonfigurasi perutean.



Menyiapkan perutean



1. Ubah strategi Router. onSameUrlNavigation



onSameUrlNavigation dapat mengambil nilai berikut:



onSameUrlNavigation: 'reload' | 'ignore';


Untuk kepekaan terhadap transisi ke url saat ini, Anda perlu menyetel 'muat ulang'.



Perubahan kebijakan tidak memuat ulang, tetapi menghasilkan peristiwa navigasi tambahan. Itu dapat diperoleh melalui langganan:



this.router.events.subscribe();


2. Ubah strategi Rute. runGuardsAndResolvers



runGuardsAndResolvers dapat mengambil nilai berikut:



type RunGuardsAndResolvers = 'pathParamsChange' | 'pathParamsOrQueryParamsChange' | 'paramsChange' | 'paramsOrQueryParamsChange' | 'always' | ((from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot) => boolean);


Untuk kepekaan terhadap transisi ke url saat ini, Anda perlu menyetel 'selalu'.



Mengonfigurasi Perutean Selama Konfigurasi Aplikasi



onSameUrlNavigation:



const routes: : Route[] = [];
@NgModule({
  imports: [
    RouterModule.forRoot(
      routes,
      { onSameUrlNavigation: 'reload' }
    )
  ]
})


runGuardsAndResolvers:



const routes: Route[] = [
  {
    path: '',
    component: AppComponent,
    runGuardsAndResolvers: 'always',
  }
];


Mengonfigurasi Perutean saat Waktu Proses



constructor(
  private router: Router,
  private route: ActivatedRoute
) {
  this.router.onSameUrlNavigation = 'reload';
  this.route.routeConfig.runGuardsAndResolvers = 'always';
}


Memulai kembali penjaga



Untuk memulai ulang pelindung satu halaman tertentu, cukup dengan mengonfigurasi perutean selama konfigurasi.



Tetapi untuk memuat ulang penjaga halaman mana pun, mengubah runGuardsAndResolvers di setiap Rute akan menyebabkan pemeriksaan yang tidak perlu. Dan kebutuhan untuk selalu mengingat parameter ini - untuk kesalahan.



Karena kasus kami mengasumsikan restart untuk halaman apa pun tanpa batasan dalam menyiapkan aplikasi, Anda memerlukan:



1. Ganti onSameUrlNavigation dan pertahankan nilai saat ini



//   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. Dapatkan ActivatedRoute untuk url saat ini



Karena ActivatedRoute yang diinjeksi dijalankan dalam layanan, ActivatedRoute yang dihasilkan tidak terkait dengan url saat ini.



ActivatedRoute untuk url saat ini terletak di outlet utama terakhir dan Anda perlu menemukannya:



//   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. Ganti runGuardsAndResolvers untuk semua ActivatedRoute dan leluhurnya, pertahankan nilai saat ini



Penjaga yang membatasi akses dapat ditemukan di salah satu leluhur ActivatedRoute saat ini. Semua leluhur berada di 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. Buka url saat ini



this.router.navigateByUrl(this.router.url);


5. Kembalikan runGuardsAndResolvers dan onSameUrlNavigation ke keadaan semula



restoreRunGuards();
restoreSameUrl();


6. Gabungkan tahapan dalam satu fungsi



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();
  });
}





Semoga artikel ini bermanfaat bagi Anda. Jika ada solusi lain, saya akan senang melihatnya di komentar.



All Articles