{"id":6317,"date":"2023-01-20T14:27:36","date_gmt":"2023-01-20T14:27:36","guid":{"rendered":"https:\/\/www.ntspl.co.in\/blog\/?p=6317"},"modified":"2023-01-20T14:31:08","modified_gmt":"2023-01-20T14:31:08","slug":"advancements-in-the-angular-router","status":"publish","type":"post","link":"https:\/\/www.ntspl.co.in\/blog\/advancements-in-the-angular-router\/","title":{"rendered":"Advancements in the Angular Router"},"content":{"rendered":"<p>The Angular team has been busy making some meaningful updates to the Angular router that are available as of Angular v14.2. We\u2019re pleased to share some recent improvements. Read on to learn\u00a0more.<\/p>\n<h3>New Router API for standalone<\/h3>\n<p>We\u2019ve <a href=\"https:\/\/angular.io\/api\/router\/provideRouter\">introduced<\/a> a way to use the Router without the need for RouterModule and improved tree shaking for reduced bundle sizes. First, let\u2019s focus on the new router integration API.<\/p>\n<p>Here\u2019s how to use the router without a RouterModule. In the bootstrapApplication function, we provide the router configuration to the providers array using the provideRouter function. The function accepts an array of application routes. Here\u2019s an\u00a0example:<\/p>\n<pre>\/\/ Bootstrap the main application component with routing capabilities<\/pre>\n<pre>const appRoutes: Routes = [\u2026];\r\nbootstrapApplication(AppComponent, {\r\n  providers: [\r\n    provideRouter(appRoutes),\r\n  ]\r\n});<\/pre>\n<p>Compare this to the existing way to set up the Router in an application that required a\u00a0module:<\/p>\n<pre>const appRoutes: Routes = [\u2026];<\/pre>\n<pre>bootstrapApplication(AppComponent, {\r\n  providers: [\r\n    importProvidersFrom(RouterModule.forRoot(appRoutes)),\r\n  ]\r\n});<\/pre>\n<h3>Tree shaking<\/h3>\n<p>The new provideRouter API allows developers to tree-shake major pieces of the router API. This is the first time we\u2019ve had the ability to enable this level of tree-shaking in the Angular router. Now teams can enjoy smaller bundle\u00a0sizes.<\/p>\n<p>The design of the RouteModule API prevents bundlers from being able to remove unused Router features. With the RouteModule API the following features are included in the production bundle even if not\u00a0enabled:<\/p>\n<ul>\n<li>HashLocationStrategy\u200a\u2014\u200agenerally used for legacy routing via updating the fragment instead of the\u00a0path<\/li>\n<li>preloading, scrolling, and logic for blocking app loading until navigation finishes for\u00a0SSR.<\/li>\n<\/ul>\n<p>The new provideRouter API changes this behavior. The features in the list above are no longer included in the production bundle if they are not\u00a0enabled.<\/p>\n<p>In our testing with the new API, we found that removing these unused features from the bundle resulted in an 11% reduction in the size of the router code in the application bundle when no features are\u00a0enabled.<\/p>\n<p>Here\u2019s an example of opting in to preloading using the withPreloading function\u00a0:<\/p>\n<pre>const appRoutes: Routes = [];<\/pre>\n<pre>bootstrapApplication(AppComponent, {\r\n  providers: [\r\n    provideRouter(appRoutes, withPreloading(PreloadAllModules))\r\n  ]\r\n});<\/pre>\n<h3>Now supporting functional router\u00a0guards<\/h3>\n<blockquote><p>Router guards require too much boilerplate\u2026<br \/>\n&#8211; most Angular developers at some point,\u00a0probably<\/p><\/blockquote>\n<p>We\u2019ve received feedback from multiple developers that amounts to developers wanting less boilerplate and more productivity. Let\u2019s cover some of the exciting new changes that move us closer to this\u00a0goal.<\/p>\n<p>Functional router guards with inject() are lightweight, ergonomic, and more composable than class-based guards.<\/p>\n<p>Here\u2019s an example of functional guard that takes a component as a parameter and returns whether or not a route can be deactivated based on the component\u2019s hasUnsavedChanges property:<\/p>\n<pre>const route = {\r\n  path: \u2018edit\u2019,\r\n  component: EditCmp,\r\n  canDeactivate: [\r\n    (component: EditCmp) =&gt; !component.hasUnsavedChanges\r\n  ]\r\n};<\/pre>\n<p>Additionally, these functional guards can still inject dependencies with inject from @angular\/core\u00a0:<\/p>\n<pre>const route = {\r\n  path: \u2018admin\u2019,\r\n  canActivate: [() =&gt; inject(LoginService).isLoggedIn()]\r\n};<\/pre>\n<p>The Router APIs previously required guards and resolvers to be present in Angular\u2019s dependency injection. This resulted in unnecessary boilerplate code. Let\u2019s consider two examples.<\/p>\n<p>In the first example, here\u2019s the code required to provide a guard using an <a href=\"https:\/\/angular.io\/api\/core\/InjectionToken\">InjectionToken<\/a>:<\/p>\n<pre>const UNSAVED_CHANGES_GUARD = new InjectionToken&lt;any&gt;('my_guard', {\r\n  providedIn: 'root',\r\n  factory: () =&gt; (component: EditCmp) =&gt; !component.hasUnsavedChanges\r\n});<\/pre>\n<pre>const route = {\r\n  path: 'edit',\r\n  component: EditCmp,\r\n  canDeactivate: [UNSAVED_CHANGES_GUARD]\r\n};<\/pre>\n<p>In this second example, here\u2019s the code required for providing a guards as an injectable class:<\/p>\n<pre><a href=\"http:\/\/twitter.com\/Injectable\">@Injectable<\/a>({providedIn: 'root'})\r\nexport class LoggedInGuard implements CanActivate {\r\n  constructor(private loginService: LoginService) {}<\/pre>\n<pre>  canActivate() {\r\n    return this.loginService.isLoggedIn();\r\n  }\r\n}<\/pre>\n<pre>const route = {\r\n  path: 'admin',\r\n  canActivate: [LoggedInGuard]\r\n};<\/pre>\n<p>Notice that even when we want to write a simple guard that has no dependencies as in the first example, we still have to write either an InjectionToken or an Injectable class. With functional guards developers can create guards, even guards with dependencies, with much less boilerplate.<\/p>\n<h3>Functional guards are composable<\/h3>\n<p>Functional guards are also more composable. A common feature request for the Router is to provide the option for guards to execute sequentially rather than all at once. Without this option in the Router implementing this type of behavior in an application would be difficult. This is smoother to implement with functional guards. There\u2019s even an <a href=\"https:\/\/github.com\/angular\/angular\/blob\/8546b17adec01de69bf314a959ef2d12f6638eb9\/packages\/router\/test\/integration.spec.ts#L5157-L5194\">example test<\/a> in the router code to demonstrate this behavior.<\/p>\n<p>Let\u2019s review the code to get a better understanding of how this\u00a0works:<\/p>\n<pre>function runSerially(guards: CanActivateFn[]|\r\n                             CanActivateChildFn[]): \r\n                             CanActivateFn|CanActivateChildFn {\r\n  return (\r\n          route: ActivatedRouteSnapshot, \r\n          state: RouterStateSnapshot) =&gt; {\r\n            const injector = inject(EnvironmentInjector);\r\n            const observables = guards.map(guard =&gt; {\r\n              const guardResult = injector.runInContext(\r\n                () =&gt; guard(route, state));\r\n              return wrapIntoObservable(guardResult).pipe(first());\r\n            });<\/pre>\n<pre>            return concat(\u2026observables).pipe(\r\n                     takeWhile(v =&gt; v === true), last());\r\n            };\r\n}<\/pre>\n<p>With functional guards, you can create factory-like functions that accept a configuration and return a guard or resolver function. With this pattern, we now also have <a href=\"https:\/\/github.com\/angular\/angular\/issues\/42171\">configurable guards and resolvers<\/a>, another common feature request. The runSerially function is a factory-like function that accepts a list of guard functions and returns a new\u00a0guard.<\/p>\n<pre>function runSerially(guards: CanActivateFn[]|CanActivateChildFn[]): CanActivateFn|CanActivateChildFn<\/pre>\n<p>First, we obtain a reference to the current injector\u00a0with:<\/p>\n<pre>const injector = inject(EnvironmentInjector);<\/pre>\n<p>When functional guards want to use dependency injection (DI), they must call inject synchronously. Each guard may execute asynchronously and may inject dependencies of their own. We need the reference to the current injector immediately so we can keep running each of them inside the same injection context:<\/p>\n<pre>const guardResult = injector.runInContext(() =&gt; guard(route, state));<\/pre>\n<p>This is also <a href=\"https:\/\/github.com\/angular\/angular\/blob\/8546b17adec01de69bf314a959ef2d12f6638eb9\/packages\/router\/src\/operators\/check_guards.ts#L145\">exactly<\/a> how the router enables the function guards\u00a0feature.<\/p>\n<p>Because guards can return an Observable, a Promise, or a synchronous result, we use our wrapIntoObservable helper to transform all results to an Observable and take only the first emitted\u00a0value:<\/p>\n<pre>return wrapIntoObservable(guardResult).pipe(first());<\/pre>\n<p>We take all of these updated guard functions, execute them one after another (with concat) and only subscribe to the result while each of them indicate the route can be activated (i.e, returning true):<\/p>\n<pre>concat(\u2026observables).pipe(takeWhile(v =&gt; v === true), last());<\/pre>\n<p>That\u2019s it. We enabled running functional guards sequentially with less than 10 lines of code. We can now call it in our `Route` config like\u00a0this:<\/p>\n<pre>{\r\n  path: \u2018hello-world\u2019,\r\n  component: HelloWorld,\r\n  canActivate: [runSerially(guard1, guard2, guard3)]\r\n}<\/pre>\n<h3>More to\u00a0Come<\/h3>\n<p>All of these changes are available as of Angular v14.2. We hope that you enjoy these updates to the router and we would love to hear what you think. You can find us\u00a0online.<\/p>\n<p>Thank you for continuing to be a part of the Angular community.<\/p>\n<p><a href=\"https:\/\/blog.angular.io\/advancements-in-the-angular-router-5d69ec4c032\">Advancements in the Angular Router<\/a> was originally published in <a href=\"https:\/\/blog.angular.io\/\">Angular Blog<\/a> on Medium, where people are continuing the conversation by highlighting and responding to this story.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Angular team has been busy making some meaningful updates to the Angular router that are available as of Angular v14.2. We\u2019re pleased to share some recent improvements. Read on to learn\u00a0more. New Router API for standalone We\u2019ve introduced a way to use the Router without the need for RouterModule and improved tree shaking for [&hellip;]<\/p>\n","protected":false},"author":42,"featured_media":6420,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[11],"tags":[],"class_list":["post-6317","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development"],"acf":{"custom_meta_title":"Advancement in the Angular Router","meta_description":"Explore the Newest Advancements in Angular Router and How They are Improving Web App Development. Read on to learn more!","meta_keyword":"Angular Advancement, Angular, Angular Routing","other_meta_tag":"<meta property=og:type content=\"article\" \/>\r\n<meta property=og:title content=\"Advancement in the Angular Router\"\/>\r\n<meta property=og:description content=\"Explore the Newest Advancements in Angular Router and How They are Improving Web App Development. Read on to learn more!\"\/>\r\n<meta property=\"og:image\" content=\"https:\/\/www.ntspl.co.in\/blog\/wp-content\/uploads\/2023\/01\/Advancements-Angular-router.jpg\"\/>\r\n<meta property=og:url content=\"https:\/\/www.ntspl.co.in\/blog\/advancements-in-the-angular-router\/\"\/>\r\n<meta property=og:site_name content=NTSPL \/>\r\n<meta name=\"twitter:site\" content=\"@NTSPL\">\r\n<meta name=twitter:card content=\"summary\" \/>\r\n<meta name=twitter:description content=\"Explore the Newest Advancements in Angular Router and How They are Improving Web App Development. Read on to learn more!\"\/>\r\n<meta name=twitter:title content=\"Advancement in the Angular Router\"\/>"},"_links":{"self":[{"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/posts\/6317"}],"collection":[{"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/users\/42"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/comments?post=6317"}],"version-history":[{"count":4,"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/posts\/6317\/revisions"}],"predecessor-version":[{"id":6424,"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/posts\/6317\/revisions\/6424"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/media\/6420"}],"wp:attachment":[{"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/media?parent=6317"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/categories?post=6317"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/tags?post=6317"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}