Would you like to TALK about it?

Introducing our first open-source library: @this-dot/route-config

August 09, 2021 | Originally published on the ThisDot blog

cover

Our engineers at This Dot Labs love open-source. We use open-source libraries every day in our jobs, and some of us are active contributors to several open-source projects. While we were working on an internal project, we found we were able to solve an issue that is common during application development. We love to give back to the community, therefore, we’ve decided to open-source it. If you would like to immediately jump into the code, check out our GitHub repository, or try out our demo app.


@this-dot/route-config is an Angular library that provides tools to easily set and access properties defined in the RouterModule configuration. It offers some built-in tools that work out of the box but also is easily extensible via the data property of Angular’s Route configuration object.

Retrieving custom page titles defined in currently rendered routes

One of the most common tasks during UI development is to make page titles dynamic. Of course, one could just copy-paste the same code and CSS into each route-level component, but that can be cumbersome in a large application. Setting the titles at the route configuration level helps, but how do we display them in our main component? We can use RouteConfigService to get the active route configuration properties.

First, we install the package with npm install @this-dot/route-config and then we import it into our application module.

import { RouteConfigModule } from '@this-dot/route-config';

@NgModule({
  /* other module props  */
  imports: [RouteConfigModule.forRoot() /* other modules */],
})
export class AppModule {}

The forRoot() method will set up the root injector to provide the RouteConfigService anywhere in the application. When we inject the service into a component, we can create an observable for the desired route configuration property. In our case that is the title property. Let’s use the getLeafConfig() method to get the titles. The second parameter of the method is the default fallback. If there is no such property present on the current active route-config, our observable will fall back to the value we provide here.

export class MainComponent {

  title$ = this.routeConfigService.getLeafConfig('title', 'Default Title');

  constructor(private routeConfigService: RouteConfigService) {}
}

And in our template, we just use the async pipe to display the title.

<header>
  <h1>{{ title$ | async }}</h1>
</header>
<main>
  <router-outlet></router-outlet>
</main>

Currently, all of our routes will display our fallback title. We can fix this by setting up the title property in our routes. For this example, we are going to add the title property only to the second route.

@NgModule({
  imports: [
    RouterModule.forRoot([
      // ...
      {
        path: 'second',
        component: SecondRouteComponent,
        data: {
          title: 'Second Route Title', // this title will be displayed when the /second route is opened
        },
      },
    ]),
  ],
  exports: [RouterModule],
})
export class AppRoutingModule {}

And the results:

First route Second route

Conditional display of UI elements based on routes

One of the hardest challenges we needed to tackle in our internal project was displaying items inside our hamburger menu based on specific routes. At first, we used our RouteConfigService to solve this issue, but it lead to a rather unreadable template, filled with a lot of *ngIf cases. We’ve decided to make a structural directive that would help with this situation.

Let’s use the *tdRouteTag directive to display a message if the 'show' tag is present in the route config. We already have the RouteConfigModule.forRoot() set up in our app module, but we still need to import the module into our feature module, so our directive can be used.

@NgModule({
  /* other module props  */
  imports: [RouteConfigModule /* other modules */],
})
export class RouteTagsExampleModule {}

The directive uses the routeTags property in the route config object to display elements. It works similar to *ngIf, we can set a fallback or “else” template for UI elements that need to display an alternative text if they are not visible in specific routes. We can also provide an array of tags for the directive. If any of the provided tags can be found in the route config, it is going to be displayed.

<router-outlet></router-outlet>

<ng-container *tdRouteTag="['show', 'business']; else noShowTag">
  <p>This text is only visible, if there is a 'routeTag.SHOW' in the route data</p>
</ng-container>
<ng-template #noShowTag>
  <p>There is no show tag in this route's config</p>
</ng-template>

Let’s set up our routes. For the second route, we intentionally add a typo, just to check if the fallback template works.

@NgModule({
  imports: [
    RouterModule.forRoot([
      {
        path: 'first',
        component: FirstRouteComponent,
        data: {
          routeTags: ['show'], // this will render the element that has *tdRouteTag="'show'"
        },
      },
      {
        path: 'second',
        component: SecondRouteComponent,
        data: {
          title: 'Second Route Title',
          routeTags: ['busines'] // "accidental" typo, so the fallback template gets rendered
        },
      },
    ]),
  ],
  exports: [RouterModule],
})
export class AppRoutingModule {}

Everything seems to be in order on the first route.

First route

But the second route displays the fallback value because of our “typo”.

Second route

Safe tags

In order to avoid such mistakes caused by typos, we can use enums. Let’s set up our enums and use them in the route config.

export enum AppRouteTags {
  SHOW = 'show',
  BUSINESS = 'business',
}

@NgModule({
  imports: [
    RouterModule.forRoot([
      {
        path: 'first',
        component: FirstRouteComponent,
        data: {
          routeTags: [AppRouteTags.SHOW],
        },
      },
      {
        path: 'second',
        component: SecondRouteComponent,
        data: {
          title: 'Second Route Title',
          routeTags: [AppRouteTags.BUSINESS]
        },
      },
    ]),
  ],
  exports: [RouterModule],
})
export class AppRoutingModule {}

Let’s use the enums in our template as well.

//...
export class RouteTagsShowcaseComponent {

  AppRouteTags = AppRouteTags
  // ...
}

<router-outlet></router-outlet>

<ng-container *tdRouteTag="[AppRouteTags.SHOW, AppRouteTags.BUSINESS]; else noShowTag">
  <p>This text is only visible, if there is a 'routeTag.SHOW' in the route data</p>
</ng-container>
<ng-template #noShowTag>
  <p>There is no show tag in this route's config</p>
</ng-template>

```

Using enums ensures that everything gets displayed properly.


This is our first open-source library, and we plan on adding more and more useful tools in the future. Do you have questions, feedback? You can reach out to us at opensource@thisdot.co.


This Dot Labs is a modern web consultancy focused on helping companies realize their digital transformation efforts. For expert architectural guidance, training, or consulting in React, Angular, Vue, Web Components, GraphQL, Node, Bazel, or Polymer, visit thisdotlabs.com.

This Dot Media is focused on creating an inclusive and educational web for all. We keep you up to date with advancements in the modern web through events, podcasts, and free content. To learn, visit thisdot.co.


Balázs Tápai

Written by Balázs Tápai.
I will make you believe that I'm secretly three senior engineers in a trench-coat. I overcome complex issues with ease and complete tasks at an impressive speed. I consistently guide teams to achieve their milestones and goals. I have a comprehensive skill set spanning from requirements gathering to front-end, back-end, pipelines, and deployments. I do my best to continuously grow and increase my capabilities.

You can follow me on Twitter or Github.