angulartypescriptangular2-routingangular19

Angular 19 - Routing not working with <a> element


I'm developing a simple app to practice with Angular and I'm hitting a wall regarding routing.

I'm not using standalone component.

I have a app.routing.module as follow:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { MonthDetailComponent } from './containers/month-detail/month-detail.component';
import { BudgetEntryComponent } from './components/budget-entry/budget-entry.component';

const routes: Routes = [
  {
    path: 'month/:id',
    component: MonthDetailComponent
  },
  {
    path: 'entry/:id',
    component: BudgetEntryComponent
  },
  {
    path: '',
    pathMatch: 'full',
    redirectTo: 'month/1'
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Here is my app.module:

import { AppComponent } from './app.component';
import { MonthListComponent } from './containers/month-list/month-list.component';
import { MonthDetailComponent } from './containers/month-detail/month-detail.component';
import { BudgetEntryComponent } from './components/budget-entry/budget-entry.component';
import { MatTableModule } from '@angular/material/table';

@NgModule({
  declarations: [
    AppComponent,
    MonthListComponent,
    MonthDetailComponent,
    BudgetEntryComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    MatTableModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

I declared the router-outlet in the app.component.html:

<budget-month-list class="menu"></budget-month-list>

<router-outlet class="content"></router-outlet>

And here is how the routerlink is used:

<div>
    <ul>
        <ng-container *ngIf="months?.length; then items; else nothing"></ng-container>      
        <ng-template #items>
            <li *ngFor="let entry of months">
                <a
                    [routerLink]="['/month', entry.id]">{{entry.name}}</a>
            </li>
        </ng-template>
        <ng-template #nothing>
            <p>No month...</p>
        </ng-template>
    </ul>
</div>

When the app starts, I arrived on the correct default value ("month/1").But when I click on one of the <a> element, I do see the URL is changed but nothing happens. If I go manually in the URL and press enter, then the routing is fired and my page loads.

For example, the app starts and I'm at "/month/1" and see the informations regarding id 1. I click on the <a> element for the id 3. I see the URL turned to "/month/3" but nothing is loaded. If I go in the URL bar and hit enter, then I'll see the information for "/month/3".

I have googled and try multiple thing on my end, but nothing works. Any idea what I may be missing?

EDIT:

Here is the MonthDetailComponent:

import { Component, OnInit } from '@angular/core';
import { BudgetEntry } from '../../models/budget-entry.model';
import { ActivatedRoute } from '@angular/router';
import { MonthService } from '../../services/month.service';

@Component({
  selector: 'budget-month-detail',
  standalone: false,
  templateUrl: './month-detail.component.html',
  styleUrl: './month-detail.component.scss'
})
export class MonthDetailComponent implements OnInit {
  columnsToDisplay = ['date', 'amount', 'description']
  budgetEntries?: BudgetEntry[];

  constructor(private route: ActivatedRoute,
              private monthService: MonthService) {
    
  }

  ngOnInit(): void {
    const id = Number(this.route.snapshot.paramMap.get('id')!)

    this.budgetEntries = this.monthService.GetMonthEntries(id);
  }
}

When I click on an anchor tag, I don't reach the ngOnInit and nothing prints in the console.


Solution

  • From the ActivatedRouteSnapshot,

    Contains the information about a route associated with a component loaded in an outlet at a particular moment in time.

    Hence, as you mention, even though the parameter in the URL changes, the id is still remained, and you only get the latest id from the URL when the component is destroyed and re-created.

    You should subscribe to the route.params for listening to URL (params) changes.

    this.route.params.subscribe({
      next: (params) => {
        const id = Number(params['id']);
    
        this.budgetEntries = this.monthService.GetMonthEntries(id);      
      },
    });
    

    Demo @ StackBlitz