loading...
Cover image for Angular in React Terms: Application Routing

Angular in React Terms: Application Routing

glebirovich profile image Gleb Irovich ・5 min read

Angular in React Terms (3 Part Series)

1) Angular in React Terms: Components & Data Flow 2) Angular in React Terms: Component State 3) Angular in React Terms: Application Routing

In today's episode, I would like to talk about application routing. We will look into some basics of working with Router in Angular and React and consider how those routers are different.

Angular being a "batteries included" framework is supplied with a Router module already. Therefore starting to use a router is a matter of importing that module. However, for React development, there are a couple of different routing libraries available. In this post, we will use react-router which is, probably, the most well-known among them.

Static vs Dynamic Routing

Before we dive into comparing routing implementation in the apps, it's essential to understand the fundamental difference between the two routers.

Angular Router provides static routing. That means that all routes are declared during the app initialisation. Depending on the app size routing is usually defined either for the entire project in the app-routing.module.ts or for every view module individually.

React Router (since v4), on the other side, employs a concept of dynamic routing. Routes are declared during the application render. Every router piece is a React component which makes it very modular and flexible. Dynamic routing allows changing route structure on the fly, for example, it can change based on some conditional logic.

Simple routing

Now let's look at the Angular and React routing in action. Let's imagine that we are building an application for an e-commerce store. It will have a home page, product catalogue and a page for every product with a short description.

In Angular we create our HomePageComponent and ProductsPageComponent as well as provide routing definition in app-routing.module.ts.

// Angular
// app.component.ts
@Component({
  selector: 'app-home-page',
  template: `<div>I am a home page</div>`,
})
export class HomePageComponent {}

@Component({
  selector: 'app-product-page',
  template: `
    <ul>
      <li *ngFor="let product of products">{{ product.value }}</li>
    </ul>
  `,
})
export class ProductsPageComponent {
  public products = [
    { id: 1, value: 'candies' },
    { id: 2, value: 'ice cream' },
  ];
}

@Component({
  selector: 'app-root',
  template: `
    <nav>
      <ul>
        <li><a routerLink="/">Home</a></li>
        <li><a routerLink="/products">Products</a></li>
      </ul>
    </nav>
    <router-outlet></router-outlet>
  `,
})
export class AppComponent {}
// Angular
// app-routing.module.ts
const routes: Routes = [
  {
    path: '',
    component: HomePageComponent,
  },
  {
    path: 'products',
    component: ProductsPageComponent,
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}
// Angular
// app.module.ts
@NgModule({
  declarations: [AppComponent, HomePageComponent, ProductsPageComponent],
  // Now we can use our routing module in the app.
  imports: [CommonModule, BrowserModule, AppRoutingModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

Here is how Angular Router works:

  1. Router listens to the changes in the URL
  2. If there is a match with one of the defined paths, Router will execute route-guards and resolvers
  3. Then a component specified in the component field will be rendered in the router-outlet

router-outlet is a special Angular component, which, similarly to ng-content, is used as a placeholder for the content matching current URL.

// React
const HomePage = () => {
  return <div>I am a home page</div>
}

const ProductsPage = () => {
  const products = [{id: 1, value: "candies"}, {id: 2, value: "ice cream"}]
  return (
    <ul>
      { products.map((product) => <li key={product.id}>{product.value}</li> ) }
    </ul>
  )
}

function App() {
  return (
    <BrowserRouter>
      <nav>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to="/products">Products</Link></li>
        </ul>
      </nav>

      <Switch>
        <Route path="/products"><ProductsPage /></Route>
        <Route path="/"><HomePage /></Route>
      </Switch>
    </BrowserRouter>
  );
}

export default App;

To enable routing in React, we need to wrap our application with BrowserRouter and define routes in the Switch. Anything added inside the Route component, will be rendered if route path matches the URL.

Redirects

Now let's assume we want our home page to be available on the /home path instead of the root. Let's update the routing and make sure, if the user visits the root she is redirected to the proper home page.

// Angular
// app-routing.module.ts
const routes: Routes = [
  {
    path: '',
    redirectTo: 'home',
    pathMatch: 'full',
  },
  {
    path: 'products',
    component: ProductsPageComponent,
  },
  {
    path: 'home',
    component: HomePageComponent,
  },
];
// React
function App() {
  return (
    <BrowserRouter>
      <nav>
        <ul>
          <li><Link to="/home">Home</Link></li>
          <li><Link to="/products">Products</Link></li>
        </ul>
      </nav>

      <Switch>
        <Route path="/products"><ProductsPage /></Route>
        <Route path="/home"><HomePage /></Route>
        <Redirect from="/" to="/home" />
      </Switch>
    </BrowserRouter>
  );
}

Unlike an additional route definition in Angular, In React, Redirect is a component from the react-router library, which will handle redirects for your app.

Route parameters

Application routing is especially helpful when we want to share some state across the application via the URL parameters. To enable dynamic parameters we only have to add a new route listener with a parameter name. Both Angular and React use :paramName syntax to map value to the parameter name.

// Angular
// app-routing.module.ts
const routes: Routes = [
    ...
  {
    path: 'products',
    children: [
      { path: '', component: ProductsPageComponent },
      { path: ':productId', component: SingleProductPageComponent },
    ],
  },
    ...
];
// Angular
// app.component.ts
...
@Component({
  selector: 'app-product-page',
  template: `
    <ul>
      <li *ngFor="let product of products" [routerLink]="product.id">
                <!-- routerLink is a directive which helps navigating application router -->
        <a [routerLink]="product.id">{{ product.value }}</a>
      </li>
    </ul>
  `,
})
export class ProductsPageComponent {
  public products = [
    { id: 1, value: 'candies' },
    { id: 2, value: 'ice cream' },
  ];
}

@Component({
  selector: 'app-single-product-page',
  template: ` <div>{{ product$ | async | json }}</div> `,
})
export class SingleProductPageComponent {
  product$ = this.activatedRoute.paramMap.pipe(
    map((params) => params.get('productId')),
    map((id) => this.products[id])
  );

  // Data about the product might be coming from the API or from the application state.
  private products = {
    1: {
      name: 'candies',
      description: 'candies are sweet',
    },
    2: {
      name: 'ice cream',
      description: 'ice cream is cold',
    },
  };

  constructor(private activatedRoute: ActivatedRoute) {}
}
// React
...
const ProductsPage = () => {
  const products = [{id: 1, value: "candies"}, {id: 2, value: "ice cream"}]
  return (
    <ul>
      { products.map((product) =>  (
        <li key={product.id}>
          {/* We can use Link component to navigate application router */}
          <Link to={`/products/${product.id}`}>{product.value}</Link>
        </li>
      )) }
    </ul>
  )
}

const SingleProductPage = () => {
  // useParams hooks help accessing router context and retrieving parameter values
  const { productId } = useParams()
  // Data about the product might be coming from the API or from the application state.
  const products = {
    1: {
      name: 'candies',
      description: 'candies are sweet',
    },
    2: {
      name: 'ice cream',
      description: 'ice cream is cold',
    },
  };
  return <div>{JSON.stringify(products[productId])}</div>
}

function App() {
  return (
    <BrowserRouter>
      <nav>
        <ul>
          <li><Link to="/home">Home</Link></li>
          <li><Link to="/products">Products</Link></li>
        </ul>
      </nav>

      <Switch>
        <Route path="/products/:productId"><SingleProductPage /></Route>
        <Route path="/products"><ProductsPage /></Route>
        <Route path="/home"><HomePage /></Route>
        <Redirect from="/" to="/home" />
      </Switch>
    </BrowserRouter>
  );
}

As you can see, using route parameters is very similar in Angular and React. To dynamically navigate inside the app, we are using routerLink directive in Angular and Link component in React. The only significant difference is accessing route parameters from the component.

In Angular we have to inject ActivatedRoute, a special class that keeps the information about the current route and its available parameters. React Router on the other side uses Context API which can be accessed by hooks (like in the example above).

Summary

Although router implementations for Angular and React are different, using them in the application is very similar. To make it work we need just three pieces of code:

  • Routes definition
  • Components that are rendered when the route is activated
  • Access to the ActivatedRoute or RouterContext if you want to retrieve parameters

Thanks for reading! If you like my posts help to spread the word and follow me on Twitter 🚀

Angular in React Terms (3 Part Series)

1) Angular in React Terms: Components & Data Flow 2) Angular in React Terms: Component State 3) Angular in React Terms: Application Routing

Posted on by:

glebirovich profile

Gleb Irovich

@glebirovich

Tech Junkie | Pizza Lover 🍕 | Frontend Wizard 🧙‍♂️ | React Addicted 🚀

Discussion

markdown guide