Creare un'applicazione multipagina con Angular e Bootstrap 5

Con RouterModule è possibile sviluppare una web app composta da più pagine.

Pubblicato da ,
Ultima modifica

In questo articolo vedremo come iniziare per creare un'applicazione Angular composta da più pagine, per approfondire questo argomento fare riferimento alla seguente documentazione

angular.io/guide/router-tutorial

L'applicazione di esempio è composta da quattro pagine Home, About, Contact e Page Not Found, e implementa Bootstrap 5 per personalizzare i vari componenti, come la barra del menù, il layout, ect...

Iniziamo quindi col creare tramite CLI i quattro Componenti Angular delle pagine HomeComponent, AboutComponent, ContactComponent, PageNotFoundComponent e il componente NavbarComponent per il menù di navigazione, per creare un nuovo componente si usa la seguente sintassi

ng generate component < nome componente >

quindi nel nostro caso

ng generate component home

ng generate component about

ng generate component contact

ng generate component page-not-found

ng generate component navbar

includiamo css e js di bootstrap 5 nel file index.html

index.html

<!DOCTYPE html>
<html lang="en">
   <head>
      <!-- Required meta tags -->
      <meta charset="utf-8" />
      <meta name="viewport" content="width=device-width, initial-scale=1" />
      <!-- Bootstrap CSS -->
      <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous" />
      <title>Angular Multipage</title>
   </head>
   <body>
      <div class="container">
         <div class="row">
            <div class="col">
               <my-app>loading</my-app>
            </div>
         </div>
      </div>
      <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4" crossorigin="anonymous"></script>
   </body>
</html>

quindi creiamo il menù modificando navbar.component.html, in particolare usiamo la direttiva routerLink per definire i link delle pagine

navbar.component.html

<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-dark">
   <div class="container-fluid">
      <a class="navbar-brand" routerLink="/">Angular Multipage</a>
      <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarText">
         <ul class="navbar-nav me-auto mb-2 mb-lg-0">
            <li class="nav-item">
               <a class="nav-link" routerLink="/">Home</a>
            </li>
            <li class="nav-item">
               <a class="nav-link" routerLink="/about">About</a>
            </li>
            <li class="nav-item">
               <a class="nav-link" routerLink="/contact">Contact</a>
            </li>
         </ul>
      </div>
   </div>
</nav>

importiamo RouterModule in app.module.ts e definiamo le rotte

app.module.ts

.....
.....
import { RouterModule } from '@angular/router';


@NgModule({
  imports: [
    .....
    .....
    RouterModule.forRoot([
      { path: '', component: HomeComponent },
      { path: 'about', component: AboutComponent },
      { path: 'contact', component: ContactComponent },
      { path: '**', component: PageNotFoundComponent }
    ])
  ],
.....
.....
})
export class AppModule {}

infine modifichiamo app.component.html includendo il menù di navigazione e la direttiva router-outlet

app.component.html

<app-navbar></app-navbar>
<router-outlet></router-outlet>

Tuttavia se testiamo l'applicazione fin qui creata, possiamo notare che se siamo su un dispositivo mobile e clicchiamo su un link della barra di navigazione, questa rimane aperta pur cambiando correttamente pagina

 

aria-expanded true

 

in pratica l'attributo aria-expanded del bottone con classe .navbar-toggler rimane sul valore true, questo perchè in Angular quando si passa da una pagina all'altra, la pagina non viene aggiornata nel browser, ecco una possibile soluzione

navbar.component.ts

.............
.............


export class NavbarComponent implements OnInit {
  expanded = false;
  constructor() {}

  ngOnInit() {}

  setExpanded() {
    if (!this.expanded) {
      this.expanded = true;
    } else {
      this.expanded = false;
    }
  }

  NotExpanded() {
    if (this.expanded) {
      this.expanded = false;
    }
  }
}

nel componente navbar settiamo una variabile expanded inizialmente su false e due funzioni setExpanded(), NotExpanded(), la prima setterà la variabile expanded su true se la navbar si è espansa, su false se la navbar si chiude, mentre la seconda funzione setta il valore di expanded su false.

Adesso modifichiamo il template del componente navbar utilizzando queste due funzioni in un evento click

navbar.component.html

<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-dark">
  <div class="container-fluid">
    <a class="navbar-brand" routerLink="/">Angular Multipage</a>
    <button class="navbar-toggler" (click)="setExpanded()" type="button" data-bs-toggle="collapse"
    data-bs-target="#navbarText"
    aria-controls="navbarText"
    aria-expanded="false"
    aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarText">
      <ul class="navbar-nav me-auto mb-2 mb-lg-0">
        <li class="nav-item">
          <a class="nav-link" (click)="NotExpanded()" [attr.aria-expanded]="expanded ? false : null"
            [attr.data-bs-target]="expanded ? '#navbarText' : null" [attr.data-bs-toggle]="expanded ? 'collapse' : null"
            routerLink="/">Home</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" (click)="NotExpanded()" [attr.aria-expanded]="expanded ? false : null"
            [attr.data-bs-target]="expanded ? '#navbarText' : null" [attr.data-bs-toggle]="expanded ? 'collapse' : null"
            routerLink="/about">About</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" (click)="NotExpanded()" [attr.aria-expanded]="expanded ? false : null"
            [attr.data-bs-target]="expanded ? '#navbarText' : null" [attr.data-bs-toggle]="expanded ? 'collapse' : null"
            routerLink="/contact">Contact</a>
        </li>
      </ul>
    </div>
  </div>
</nav>

in questo modo assegneremo gli attributi aria-expanded, data-bs-target e data-bs-toggle ai rispettivi link in base al valore della variabile expanded

[attr.aria-expanded]="expanded ? false : null"
     [attr.data-bs-target]="expanded ? '#navbarText' : null" 
          [attr.data-bs-toggle]="expanded ? 'collapse' : null"

 

Code on Stackblitz

View Demo