Skip to content

Routing Management with LitElement and TypeScript

Routing Management with LitElement and TypeScript

Most modern frameworks provide a routing mechanism to handle the navigation between views within the application. This is a critical feature for a Single Page Application (SPA).

This is where the Route Matching comes in. When an SPA relies on a router, you'll need to define a set of routes with the supported locations.

In the JavaScript world, most of route definitions are inspired by Express Routing:

const routes = [
  { path: '/'}, 
  { path: 'posts'}, 
  { path: 'posts/:id'},
  { path: 'about'}
]

In this example, the application will render a view associated with the specific location that "matches" with the path value.

router-diagram

Getting Started

In my last post, I explained how to get started with LitElement and TypeScript. Taking those concepts and tools into account, let's describe the problem we're going to solve.

A Personal Website

Let's build a personal website with blogging support as an SPA using LitElement and TypeScript. We can think in the following requirements for the project (in terms of routing):

  • Display the list of blog posts as the home page
  • Display a single view with the details of an article
  • Allow displaying an about page
  • Display a portfolio section, including projects and activities

Creating the Project

Let's use a project generator by open-wc initiative. Run the following commands and follow the instructions:

npm init @open-wc
# Select "Scaffold a new project" (What would you like to do today?)
# Select "Application" (What would you like to scaffold?)
# Mark/Select "Linting", "Testing", "Demoing" and "Building" (What would you like to add?)
# Yes (Would you like to use TypeScript?)
# Mark/Select "Testing", "Demoing" and "Building" (Would you like to scaffold examples files for?)
# litelement-website (What is the tag name of your application/web component?)
# Yes (Do you want to write this file structure to disk?)
# Yes, with npm (Do you want to install dependencies?)

After finishing the project creation, run the following command to start the preview with live-reloading support:

npm run start

The previous command will perform some useful taks in development mode:

  • Start the compilation of TypeScript files through tsc(the TypeScript compiler)
  • Starts a web server for development with fast reloads. The input file is index.html.
  • The web server will reload the browser when files are edited

Web Components Routing

So you already have an initial project with LitElement and TypeScript support. It's time to choose a strategy for routing handling since we're working with LitElement to build the Web Components.

You can find several libraries to add the routing support. Among others, I found vaadin-router as an interesting option.

Vaadin Router

According to the official website:

A small, powerful and framework-agnostic client-side router for Web Components

Enough has been said: vaadin-router is one of the most popular choices today. It has good examples, documentation, and even better: it's a small library for your client-side JavaScript projects.

When using this library with TypeScript, you'll have type declarations available too.

Install the Router Library

Keep in mind you're not installing a whole framework: only a single library to get covered on routing requirements in your app.

npm install --save @vaadin/router

The Routing Configuration

Now think in the following project structure and let's assume we'll have a Web Component on every file inside blog directory:

|- index.html
|- src/
    |- index.ts
    |- app.ts
    |- blog/
        |- blog.ts
        |- blog-posts.ts
        |- blog-post.ts

In that way, we can define the routes configuration as a parent-children tree:

// index.ts
import { Router } from '@vaadin/router';
import './app'; // Adding the lit-app component here for better performance

const routes = [
  {
    path: '/',
    component: 'lit-app',
    children: [
      {
        path: 'blog',
        component: 'lit-blog',
        action: async () => {
          await import('./blog/blog');
        },
        children: [
          {
            path: '',
            redirect: '/blog/posts',
          },
          {
            path: 'posts',
            component: 'lit-blog-posts',
            action: async () => {
              await import('./blog/blog-posts');
            },
          },
          {
            path: 'posts/:id',
            component: 'lit-blog-post',
            action: async () => {
              await import('./blog/blog-post');
            },
          },
        ]
      },
      {
        path: 'about',
        component: 'lit-about',
        action: async () => {
          await import('./about/about');
        },
      },
    ]
  },
];

const outlet = document.getElementById('outlet');
export const router = new Router(outlet);
router.setRoutes(routes);

This configuration means:

  • If the home page is visited / the app will start loading lit-app component, which is defined in app.ts file.
  • Once /blog path is loaded, it will display lit-blog-posts component by default: {path: '', redirect: '/blog/posts'}
  • In order to see a blog post, you can access to /blog/posts/:id path, where :id represents the blog post identifier(this is a required parameter to have a "match").
  • The /about path will load another component.

Another important note here, instead of loading all components at startup, the route configuration is using Dynamic Imports:

{
  ...
  action: async () => {
    await import('./file');
  }
}

As you can see in this example, Vaadin Router makes it feasible to implement your custom loading mechanism through Custom Route Actions.

The index.html File

Pay attention on the body section:

<body>
  <div id="outlet"></div>
  <script type="module" src="./out-tsc/src/index.js"></script>
</body>

The first line <div id="outlet"></div> is the starting point for the routing configuration since the Router class will take this element as an input.

Also, the second line <script type="module" src="./out-tsc/src/index.js"></script> will start loading the provided routes from index.ts file.

Create the Components

Now let's create lit-app and lit-blog as our first components:

// app.ts

import { LitElement, html, customElement, css } from 'lit-element';

@customElement('lit-app')
export class App extends LitElement {
  static styles = css`
    .header {
      padding: 20px;
      font-size: 25px;
      text-align: center;
      background: white;
    }

    .topnav {
      background-color: #4f4c4c;
      overflow: hidden;
    }

    .topnav a {
      float: left;
      color: #f2f2f2;
      text-align: center;
      padding: 14px 16px;
      text-decoration: none;
      font-size: 17px;
    }

    .topnav a:hover {
      background-color: #ddd;
      color: black;
    }

    .topnav a.active {
      background-color: #008CBA;
      color: white;
    }
  `;

  render() {
    return html`
      <div class="topnav">
        <a class="active" href="/">Home</a>
        <a href="/blog">Blog</a>
        <a href="/about">About</a>
      </div>
      <div class="header">
        <h2>LitElement Website</h2>
      </div>

      <slot></slot>
    `;
  }
}
// blog.ts
import { LitElement, html, customElement } from 'lit-element';

@customElement('lit-blog')
export class Blog extends LitElement {
  render() {
    return html`
      <slot></slot>
    `;
  }
}

As you can see in the previous components, the HTML template uses <slot></slot> as a placeholder to specify a place where you can put your own content according to the current path.

Next, define the lit-blog-posts component that will display a list of blog posts as follows:

// blog-posts.ts

import { LitElement, html, customElement, css } from 'lit-element';

@customElement('lit-blog-posts')
export class BlogPosts extends LitElement {
  render() {
    return html`
      <h2>Blog Posts</h2>
      <ul>
        <li><a href="/blog/posts/1">LitElement Introduction</a></li>
        <li><a href="/blog/posts/1">Secrets of JavaScript</a></li>
        <li><a href="/blog/posts/1">Web Components and TypeScript</a></li>
      </ul>
    `;
  }
}

In the same way, define the lit-blog-post component to see the detail of any blog post displayed in the previous list.

// blog-post.ts

import { LitElement, html, customElement } from 'lit-element';

@customElement('lit-blog-post')
export class BlogPost extends LitElement {
  render() {
    return html`
      <h2>Blog Post Title</h2>
      <p>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec felis
        est, placerat ut risus non, bibendum tincidunt nisl. Sed vitae gravida
        urna. Maecenas ut efficitur massa, sed viverra dolor. Ut euismod, nibh
        vel suscipit porttitor, augue libero dictum lacus, et pellentesque enim
        libero quis dui. Curabitur lorem sapien, tristique eget dictum non,
        lobortis ac justo. Ut ac ipsum aliquam, vehicula metus eu, vulputate
        felis. Nunc commodo viverra dolor commodo viverra. Donec et leo diam.
        Duis iaculis cursus bibendum. Vivamus a venenatis turpis. Proin ultrices
        libero vel sollicitudin condimentum. Curabitur vitae nisl id orci
        placerat imperdiet. In eget orci leo. Fusce dignissim, orci nec
        fermentum lobortis, ligula massa bibendum mauris, at imperdiet velit
        purus a dolor. Donec et tempor ante.
      </p>
    `;
  }
}

Final Result

You'll end with a web application written in TypeScript and using Web Components as follows:

litelement-website

Source Code Project

Find the complete project in this GitHub repository: https://github.com/luixaviles/litelement-website. Do not forget to give it a star ⭐️ and play around with the code.

You can follow me on Twitter and GitHub to see more about my work.

This Dot Labs is a development consultancy that is trusted by top industry companies, including Stripe, Xero, Wikimedia, Docusign, and Twilio. This Dot takes a hands-on approach by providing tailored development strategies to help you approach your most pressing challenges with clarity and confidence. Whether it's bridging the gap between business and technology or modernizing legacy systems, you’ll find a breadth of experience and knowledge you need. Check out how This Dot Labs can empower your tech journey.

You might also like

Testing a Fastify app with the NodeJS test runner cover image

Testing a Fastify app with the NodeJS test runner

Introduction Node.js has shipped a built-in test runner for a couple of major versions. Since its release I haven’t heard much about it so I decided to try it out on a simple Fastify API server application that I was working on. It turns out, it’s pretty good! It’s also really nice to start testing a node application without dealing with the hassle of installing some additional dependencies and managing more configurations. Since it’s got my stamp of approval, why not write a post about it? In this post, we will hit the highlights of the testing API and write some basic but real-life tests for an API server. This server will be built with Fastify, a plugin-centric API framework. They have some good documentation on testing that should make this pretty easy. We’ll also add a SQL driver for the plugin we will test. Setup Let's set up our simple API server by creating a new project, adding our dependencies, and creating some files. Ensure you’re running node v20 or greater (Test runner is a stable API as of the 20 major releases) Overview `index.js` - node entry that initializes our Fastify app and listens for incoming http requests on port 3001 `app.js` - this file exports a function that creates and returns our Fastify application instance `sql-plugin.js` - a Fastify plugin that sets up and connects to a SQL driver and makes it available on our app instance Application Code A simple first test For our first test we will just test our servers index route. If you recall from the app.js` code above, our index route returns a 501 response for “not implemented”. In this test, we're using the createApp` function to create a new instance of our Fastify app, and then using the `inject` method from the Fastify API to make a request to the `/` route. We import our test utilities directly from the node. Notice we can pass async functions to our test to use async/await. Node’s assert API has been around for a long time, this is what we are using to make our test assertions. To run this test, we can use the following command: By default the Node.js test runner uses the TAP reporter. You can configure it using other reporters or even create your own custom reporters for it to use. Testing our SQL plugin Next, let's take a look at how to test our Fastify Postgres plugin. This one is a bit more involved and gives us an opportunity to use more of the test runner features. In this example, we are using a feature called Subtests. This simply means when nested tests inside of a top-level test. In our top-level test call, we get a test parameter t` that we call methods on in our nested test structure. In this example, we use `t.beforeEach` to create a new Fastify app instance for each test, and call the `test` method to register our nested tests. Along with `beforeEach` the other methods you might expect are also available: `afterEach`, `before`, `after`. Since we don’t want to connect to our Postgres database in our tests, we are using the available Mocking API to mock out the client. This was the API that I was most excited to see included in the Node Test Runner. After the basics, you almost always need to mock some functions, methods, or libraries in your tests. After trying this feature, it works easily and as expected, I was confident that I could get pretty far testing with the new Node.js core API’s. Since my plugin only uses the end method of the Postgres driver, it’s the only method I provide a mock function for. Our second test confirms that it gets called when our Fastify server is shutting down. Additional features A lot of other features that are common in other popular testing frameworks are also available. Test styles and methods Along with our basic test` based tests we used for our Fastify plugins - `test` also includes `skip`, `todo`, and `only` methods. They are for what you would expect based on the names, skipping or only running certain tests, and work-in-progress tests. If you prefer, you also have the option of using the describe` → `it` test syntax. They both come with the same methods as `test` and I think it really comes down to a matter of personal preference. Test coverage This might be the deal breaker for some since this feature is still experimental. As popular as test coverage reporting is, I expect this API to be finalized and become stable in an upcoming version. Since this isn’t something that’s being shipped for the end user though, I say go for it. What’s the worst that could happen really? Other CLI flags —watch` - https://nodejs.org/dist/latest-v20.x/docs/api/cli.html#--watch —test-name-pattern` - https://nodejs.org/dist/latest-v20.x/docs/api/cli.html#--test-name-pattern TypeScript support You can use a loader like you would for a regular node application to execute TypeScript files. Some popular examples are tsx` and `ts-node`. In practice, I found that this currently doesn’t work well since the test runner only looks for JS file types. After digging in I found that they added support to locate your test files via a glob string but it won’t be available until the next major version release. Conclusion The built-in test runner is a lot more comprehensive than I expected it to be. I was able to easily write some real-world tests for my application. If you don’t mind some of the features like coverage reporting being experimental, you can get pretty far without installing any additional dependencies. The biggest deal breaker on many projects at this point, in my opinion, is the lack of straightforward TypeScript support. This is the test command that I ended up with in my application: I’ll be honest, I stole this from a GitHub issue thread and I don’t know exactly how it works (but it does). If TypeScript is a requirement, maybe stick with Jest or Vitest for now 🙂...

I Broke My Hand So You Don't Have To (First-Hand Accessibility Insights) cover image

I Broke My Hand So You Don't Have To (First-Hand Accessibility Insights)

We take accessibility quite seriously here at This Dot because we know it's important. Still, throughout my career, I've seen many projects where accessibility was brushed aside for reasons like "our users don't really use keyboard shortcuts" or "we need to ship fast; we can add accessibility later." The truth is, that "later" often means "never." And it turns out, anyone could break their hand, like I did. I broke my dominant hand and spent four weeks in a cast, effectively rendering it useless and forcing me to work left-handed. I must thus apologize for the misleading title; this post should more accurately be dubbed "second-hand" accessibility insights. The Perspective of a Developer Firstly, it's not the end of the world. I adapted quickly to my temporary disability, which was, for the most part, a minor inconvenience. I had to type with one hand, obviously slower than my usual pace, but isn't a significant part of a software engineer's work focused on thinking? Here's what I did and learned: - I moved my mouse to the left and started using it with my left hand. I adapted quickly, but the experience wasn't as smooth as using my right hand. I could perform most tasks, but I needed to be more careful and precise. - Many actions require holding a key while pressing a mouse button (e.g., visiting links from the IDE), which is hard to do with one hand. - This led me to explore trackpad options. Apart from the Apple Magic Trackpad, choices were limited. As a Windows user (I know, sorry), that wasn't an option for me. I settled for a cheap trackpad from Amazon. A lot of tasks became easier; however, the trackpad eventually malfunctioned, sending me back to the mouse. - I don't know a lot of IDE shortcuts. I realized how much I've been relying on a mouse for my work, subconsciously refusing to learn new keyboard shortcuts (I'll be returning my senior engineer license shortly). So I learned a few new ones, which is good, I guess. - Some keyboard shortcuts are hard to press with one hand. If you find yourself in a similar situation, you may need to remap some of them. - Copilot became my best friend, saving me from a lot of slow typing, although I did have to correct and rewrite many of its suggestions. The Perspective of a User As a developer, I was able to get by and figure things out to be able to work effectively. As a user, however, I got to experience the other side of the coin and really feel the accessibility (or lack thereof) on the web. Here are a few insights I gained: - A lot of websites apparently tried_ to implement keyboard navigation, but failed miserably. For example, a big e-commerce website I tried to use to shop for the aforementioned trackpad seemed to work fine with keyboard navigation at first, but once I focused on the search field, I found myself unable to tab out from it. When you make the effort to implement keyboard navigation, please make sure it works properly and it doesn't get broken with new changes. I wholeheartedly recommend having e2e tests (e.g. with Playwright) that verify the keyboard navigation works as expected. - A few websites and web apps I tried to use were completely unusable with the keyboard and were designed to be used with a mouse only. - Some sites had elaborate keyboard navigation, with custom keyboard shortcuts for different functionality. That took some time to figure out, and I reckon it's not as intuitive as the designers thought it would be. Once a user learns the shortcuts, however, it could make their life easier, I suppose. - A lot of interactive elements are much smaller than they should be, making it hard to accurately click on them with your weaker hand. Designers, I beg you, please make your buttons bigger. I once worked on an application that had a "gloves mode" for environments where the operators would be using gloves, and I feel like maybe the size we went with for the "gloves mode" should be the standard everywhere, especially as screens get bigger and bigger. - Misclicking is easy, especially using your weaker hand. Be it a mouse click or just hitting an Enter key on accident. Kudos to all the developers who thought about this and implemented a confirmation dialog or other safety measures to prevent users from accidentally deleting or posting something. I've however encountered a few apps that didn't have any of these, and those made me a bit anxious, to be honest. If this is something you haven't thought about when developing an app, please start doing so, you might save someone a lot of trouble. Some Second-Hand Insights I was only a little bit impaired by being temporarily one-handed and it was honestly a big pain. In this post, I've focused on my anecdotal experience as a developer and a user, covering mostly keyboard navigation and mouse usage. I can only imagine how frustrating it must be for visually impaired users, or users with other disabilities, to use the web. I must confess I haven't always been treating accessibility as a priority, but I've certainly learned my lesson. I will try to make sure all the apps I work on are accessible and inclusive, and I will try to test not only the keyboard navigation, ARIA attributes, and other accessibility features, but also the overall experience of using the app with a screen reader. I hope this post will at least plant a little seed in your head that makes you think about what it feels like to be disabled and what would the experience of a disabled person be like using the app you're working on. Conclusion: The Humbling Realities of Accessibility The past few weeks have been an eye-opening journey for me into the world of accessibility, exposing its importance not just in theory but in palpable, daily experiences. My short-term impairment allowed me to peek into a life where simple tasks aren't so simple, and convenient shortcuts are a maze of complications. It has been a humbling experience, but also an illuminating one. As developers and designers, we often get caught in the rush to innovate and to ship, leaving behind essential elements that make technology inclusive and humane. While my temporary disability was an inconvenience, it's permanent for many others. A broken hand made me realize how broken our approach towards accessibility often is. The key takeaway here isn't just a list of accessibility tips; it's an earnest appeal to empathize with your end-users. "Designing for all" is not a checkbox to tick off before a product launch; it's an ongoing commitment to the understanding that everyone interacts with technology differently. When being empathetic and sincerely thinking about accessibility, you never know whose life you could be making easier. After all, disability isn't a special condition; it's a part of the human condition. And if you still think "Our users don't really use keyboard shortcuts" or "We can add accessibility later," remember that you're not just failing a compliance checklist, you're failing real people....

How to Update the Application Title based on Routing Changes in Angular cover image

How to Update the Application Title based on Routing Changes in Angular

Have you tried to update the document's title of your application? Maybe you're thinking that applying interpolation should be enough: `html {{myCustomTitleVariable}} ` That solution is not going to work since the ` element is outside of the scope of the Angular application. In fact, the root component of your app is within `` tag, and the title is part of the `` element. Luckily, Angular provides the Title service with the methods to read the current title of the application, and a setTitle(title)` to update that value. However, what happens if you need to update the title on routing changes? Also, you may consider updating it on certain components for Analytics purposes. In this blog post, I'll explain step-by-step how to create a custom Title service to have full control over the title of the current HTML document for your application. Project Setup Prerequisites You'll need to have installed the following tools in your local environment: - Node.js**. Preferably the latest LTS version. - A package manager**. You can use either NPM or Yarn. This tutorial will use NPM. Creating the Angular Project Let's assume we'll need to build an application with the following routes as requirements: `txt /home |- Renders a home component /products |- Renders a list of products /products/ |- Renders a product detail based on its Identifier The app redirects to /home path by default ` Now, let's create the project from scratch using the Angular CLI tool. `bash ng new angular-update-title --routing --prefix corp --style css --skip-tests ` This command will initialize a base project using some configuration options: - --routing`. It will create a routing module. - --prefix corp`. It defines a prefix to be applied to the selectors for created components(`corp` in this case). The default value is `app`. - --style css`. The file extension for the styling files. - --skip-tests`. it avoids the generations of the `.spec.ts` files, which are used for testing Creating the Modules and Components Once we got the initial structure of the app, we'll continue running the following commands to create a separate module for /home` and `/products`, which are the main paths of the project: `bash ng generate module home --routing ng generate component home/home ng generate module products --routing ng generate component products/products ng generate component products/product-detail ` The `--routing` flag can be using also along with `ng generate module` to create a routing configuration file for that module. Creating the Title Service Similar to the previous section, we will create a shared` module to hold the `Title` service. Both can be generated with the following commands: `bash ng generate module shared --module app ng generate service shared/services/title ` The `--module app` flag is used to "link" the brand new module to the pre-existing `app.module.ts` file. The Routing Configuration Open the app-routing.module.ts` file, and create the initial routes. `ts // app-routing.module.ts const routes: Routes = [ { path: '', pathMatch: 'full', redirectTo: 'home' }, { path: 'home', component: HomeComponent, data: { pageTitle: 'Home' } }, { path: 'products', loadChildren: () => import('./products/products.module').then(m => m.ProductsModule) } ]; ` By default, the application will redirect to the `home` path. When the router loads the `home` path, a `HomeComponent` will be rendered. The `products` path will be loaded using the _lazy loading_ feature. Pay attention to the data provided to the home` path. It contains the configured title through `pageTitle` string. Next, open the products-routing.module.ts` file to enable an additional configuration to load the _Products_ and the _Product Detail_ page. `ts // products-routing.module.ts const routes: Routes = [ { path: '', component: ProductsComponent, children: [ { path: ':id', component: ProductDetailComponent, } ], data: { pageTitle: 'Products' } }, ]; ` The router will render the `ProductsComponent` by default when the path matches to `/products`. This route also defines custom data to be rendered as titles later. When the path also adds an Id on `/products/:id`, the router will render the `ProductDetailComponent`. The Title Service Implementation It's time to implement the custom Title Service for our application. `ts // title.service.ts import { Injectable } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { BehaviorSubject, merge, Observable } from 'rxjs'; import { filter, map, tap } from 'rxjs/operators'; const DEFAULTTITLE = 'Corp'; @Injectable({ providedIn: 'root' }) export class TitleService { title$ = new BehaviorSubject(DEFAULTTITLE); private titleRoute$: Observable = this.router.events.pipe( filter((event) => event instanceof NavigationEnd), map(() => this.getPageTitle(this.activatedRoute.firstChild)) ); private titleState$ = merge(this.title$, this.titleRoute$).pipe( filter((title) => title !== undefined), tap((title) => { this.titleService.setTitle(${DEFAULT_TITLE} - ${title}`); }) ); constructor( private router: Router, private activatedRoute: ActivatedRoute, private titleService: Title ) { this.titleState$.subscribe(); } private getPageTitle( activatedRoute: ActivatedRoute | null ): string | undefined { while (activatedRoute) { if (activatedRoute.firstChild) { activatedRoute = activatedRoute.firstChild; } else if ( activatedRoute.snapshot.data && activatedRoute.snapshot.data['pageTitle'] ) { return activatedRoute.snapshot.data['pageTitle'] as string; } else { return undefined; } } return undefined; } } ` The above service implementation could be understood in just a few steps. First, we'll need to make sure to inject the `Router`, `ActivatedRoute` and `Title` services in the constructor. The `title$` attribute contains the initial value for the title("Corp"), which will be emitted through a _BehaviorSubject_. The `titleRoute$` is an Observable ready to emit any `pageTitle` value defined in the current route. It may use the parent's _pageTitle_ otherwise. The `titleState$` is an Observable ready to _listen_ to either `title$` or `titleRoute$` values. In case incoming value is defined, it will call the Angular Title service to perform the update. The `getPageTitle` method will be in charge of obtaining the `pageTitle` of the current route if it is defined or the title of the parent otherwise. Injecting the Title Service One easy way to apply the custom Title Service in the whole application is by updating the app.module.ts` file and injecting it into the constructor. `ts // app.module.ts export class AppModule { constructor(public titleService: TitleService) {} } ` In that way, once the default component gets rendered, the title will be displayed as Corp - Home`. If you click on Go to Products_ link, then a redirection will be performed and the Title service will be invoked again to display `Corp - Products` at this time. However, we may need to render a different title according to the product detail. In this case, we'll show Corp - Product Detail - :id` where the `Id` matches with the current route parameter. `ts // product-detail.component.ts import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { map, Subscription, tap } from 'rxjs'; import { TitleService } from 'src/app/shared/services/title.service'; @Component({ selector: 'corp-product-detail', templateUrl: './product-detail.component.html', styleUrls: ['./product-detail.component.css'], }) export class ProductDetailComponent implements OnInit, OnDestroy { protected subscription = new Subscription(); productId$ = this.route.params.pipe(map((params) => params['id'])); constructor( private route: ActivatedRoute, private titleService: TitleService ) {} ngOnInit(): void { const productIdSubscription = this.productId$ .pipe( tap((id) => this.titleService.title$.next(Product Detail - ${id}`)) ) .subscribe(); this.subscription.add(productIdSubscription); } ngOnDestroy(): void { this.subscription.unsubscribe(); } } ` Let's explain the implementation of this component: The constructor injects the `ActivatedRoute` and the custom `TitleService`. The `productId$` is the _Observable_ which is going to emit the `Id` parameter every time it changes in the URL. Once the component gets initialized, we'll need to _subscribe_ to the `productId$` _Observable_ and then emit a new value for the title after creating a new string using the `id`. That's possible through the `titleService.title$.next()` method. When the component gets _destroyed_, we'll need to _unsubscribe_ from the `productIdSubscription`. We're ready to go! Every time you select a product, the ProductDetail` component will be rendered, and the title will be updated accordingly. Live Demo and Source Code Want to play around with the final application? Just open the following link in your browser: https://luixaviles.github.io/angular-update-title. Find the complete angular project in this GitHub repository: angular-update-title-service. Do not forget to give it a star ⭐️, and play around with the code. Feel free to reach out on Twitter if you have any questions. Follow me on GitHub to see more about my work....

Nuxt DevTools v1.0: Redefining the Developer Experience Beyond Conventional Tools cover image

Nuxt DevTools v1.0: Redefining the Developer Experience Beyond Conventional Tools

In the ever-evolving world of web development, Nuxt.js has taken a monumental leap with the launch of Nuxt DevTools v1.0. More than just a set of tools, it's a game-changer—a faithful companion for developers. This groundbreaking release, available for all Nuxt projects and being defaulted from Nuxt v3.8 onwards, marks the beginning of a new era in developer tools. It's designed to simplify our development journey, offering unparalleled transparency, performance, and ease of use. Join me as we explore how Nuxt DevTools v1.0 is set to revolutionize our workflow, making development faster and more efficient than ever. What makes Nuxt DevTools so unique? Alright, let's start delving into the features that make this tool so amazing and unique. There are a lot, so buckle up! In-App DevTools The first thing that caught my attention is that breaking away from traditional browser extensions, Nuxt DevTools v1.0 is seamlessly integrated within your Nuxt app. This ensures universal compatibility across browsers and devices, offering a more stable and consistent development experience. This setup also means the tools are readily available in the app, making your work more efficient. It's a smart move from the usual browser extensions, making it a notable highlight. To use it you just need to press Shift + Option + D` (macOS) or `Shift + Alt + D` (Windows): With simple keystrokes, the Nuxt DevTools v1.0 springs to life directly within your app, ready for action. This integration eliminates the need to toggle between windows or panels, keeping your workflow streamlined and focused. The tools are not only easily accessible but also intelligently designed to enhance your productivity. Pages, Components, and Componsables View The Pages, Components, and Composables View in Nuxt DevTools v1.0 are a clear roadmap for your app. They help you understand how your app is built by simply showing its structure. It's like having a map that makes sense of your app's layout, making the complex parts of your code easier to understand. This is really helpful for new developers learning about the app and experienced developers working on big projects. Pages View lists all your app's pages, making it easier to move around and see how your site is structured. What's impressive is the live update capability. As you explore the DevTools, you can see the changes happening in real-time, giving you instant feedback on your app's behavior. Components View is like a detailed map of all the parts (components) your app uses, showing you how they connect and depend on each other. This helps you keep everything organized, especially in big projects. You can inspect components, change layouts, see their references, and filter them. By showcasing all the auto-imported composables, Nuxt DevTools provides a clear overview of the composables in use, including their source files. This feature brings much-needed clarity to managing composables within large projects. You can also see short descriptions and documentation links in some of them. Together, these features give you a clear picture of your app's layout and workings, simplifying navigation and management. Modules and Static Assets Management This aspect of the DevTools revolutionizes module management. It displays all registered modules, documentation, and repository links, making it easy to discover and install new modules from the community! This makes managing and expanding your app's capabilities more straightforward than ever. On the other hand, handling static assets like images and videos becomes a breeze. The tool allows you to preview and integrate these assets effortlessly within the DevTools environment. These features significantly enhance the ease and efficiency of managing your app's dynamic and static elements. The Runtime Config and Payload Editor The Runtime Config and Payload Editor in Nuxt DevTools make working with your app's settings and data straightforward. The Runtime Config lets you play with different configuration settings in real time, like adjusting settings on the fly and seeing the effects immediately. This is great for fine-tuning your app without guesswork. The Payload Editor is all about managing the data your app handles, especially data passed from server to client. It's like having a direct view and control over the data your app uses and displays. This tool is handy for seeing how changes in data impact your app, making it easier to understand and debug data-related issues. Open Graph Preview The Open Graph Preview in Nuxt DevTools is a feature I find incredibly handy and a real time-saver. It lets you see how your app will appear when shared on social media platforms. This tool is crucial for SEO and social media presence, as it previews the Open Graph tags (like images and descriptions) used when your app is shared. No more deploying first to check if everything looks right – you can now tweak and get instant feedback within the DevTools. This feature not only streamlines the process of optimizing for social media but also ensures your app makes the best possible first impression online. Timeline The Timeline feature in Nuxt DevTools is another standout tool. It lets you track when and how each part of your app (like composables) is called. This is different from typical performance tools because it focuses on the high-level aspects of your app, like navigation events and composable calls, giving you a more practical view of your app's operation. It's particularly useful for understanding the sequence and impact of events and actions in your app, making it easier to spot issues and optimize performance. This timeline view brings a new level of clarity to monitoring your app's behavior in real-time. Production Build Analyzer The Production Build Analyzer feature in Nuxt DevTools v1.0 is like a health check for your app. It looks at your app's final build and shows you how to make it better and faster. Think of it as a doctor for your app, pointing out areas that need improvement and helping you optimize performance. API Playground The API Playground in Nuxt DevTools v1.0 is like a sandbox where you can play and experiment with your app's APIs. It's a space where you can easily test and try out different things without affecting your main app. This makes it a great tool for trying out new ideas or checking how changes might work. Some other cool features Another amazing aspect of Nuxt DevTools is the embedded full-featured VS Code. It's like having your favorite code editor inside the DevTools, with all its powerful features and extensions. It's incredibly convenient for making quick edits or tweaks to your code. Then there's the Component Inspector. Think of it as your code's detective tool. It lets you easily pinpoint and understand which parts of your code are behind specific elements on your page. This makes identifying and editing components a breeze. And remember customization! Nuxt DevTools lets you tweak its UI to suit your style. This means you can set up the tools just how you like them, making your development environment more comfortable and tailored to your preferences. Conclusion In summary, Nuxt DevTools v1.0 marks a revolutionary step in web development, offering a comprehensive suite of features that elevate the entire development process. Features like live updates, easy navigation, and a user-friendly interface enrich the development experience. Each tool within Nuxt DevTools v1.0 is thoughtfully designed to simplify and enhance how developers build and manage their applications. In essence, Nuxt DevTools v1.0 is more than just a toolkit; it's a transformative companion for developers seeking to build high-quality web applications more efficiently and effectively. It represents the future of web development tools, setting new standards in developer experience and productivity....