angulartypescript

Problem with [routerLink] NullInjectorError: No provider for ActivatedRoute


I'm making my first web app, after taking a course. I've gotten stuck on Routes, and although I've made several changes by reading other posts, asking Chatgpt or Copilot, I can't solve the problem, which occurs when I add a [routerLink].

    client:489 [vite] connecting...
    book.component.ts:58 BookComponent initialized
    core.mjs:30079 Angular is running in development mode.
    client:608 [vite] connected.
    book.component.ts:29 (7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]
    book.component.ts:28 ERROR NullInjectorError: 
    R3InjectorError(Standalone[_AppComponent])[ActivatedRoute -> ActivatedRoute -> 
    ActivatedRoute]: 
    NullInjectorError: No provider for ActivatedRoute!
    at NullInjector.get (core.mjs:1636:21)
    at R3Injector.get (core.mjs:3018:27)
    at R3Injector.get (core.mjs:3018:27)
    at R3Injector.get (core.mjs:3018:27)
    at ChainedInjector.get (core.mjs:5289:32)
    at lookupTokenUsingModuleInjector (core.mjs:5632:31)
    at getOrCreateInjectable (core.mjs:5678:10)
    at ɵɵdirectiveInject (core.mjs:11577:17)
    at NodeInjectorFactory.RouterLink_Factory [as factory] (router.mjs:6172:85)
    at getNodeInjectable (core.mjs:5872:38)

if in the app.components.ts I delete in the middle of my html page I don't see anything so I think the correct structure I used is.

if I delete the [routerLink]from the html everything works correctly, of course I cannot go to the other component

if instead I put [routerLink] I have the text ‘I am here’ which is prior to ngFor and the form in but I don't have the list

when I launch the server I correctly enter the ngOnInit of book.components.ts

I correctly access the getAllBooks method without any problems or errors

<app-header></app-header>
<app-book></app-book>
<app-footer></app-footer>
<router-outlet></router-outlet>

angular version 18.2.11

I will add all the main code

app.components.ts

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [HeaderComponent, FooterComponent, RouterOutlet, BookComponent],
  template: `
    <app-header></app-header>
    <app-book></app-book>
    <app-footer></app-footer>
    <router-outlet></router-outlet>
  `,
})
export class AppComponent {
  books: Book | undefined;
}

app-book BookComponent

import { Component, OnInit } from '@angular/core';
import { Book } from '../../model/book';
import { HttpClientModule, HttpClient } from '@angular/common/http';
import { CommonModule } from '@angular/common';
import { FormsModule, NgForm } from '@angular/forms';
import { FormComponent } from '../../shared/form/form.component';
import { BookService } from '../../service/book.service';
import { Router, RouterModule, RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-book',
  standalone: true,
  imports: [
    HttpClientModule,
    CommonModule,
    FormsModule,
    FormComponent,
    RouterModule,
  ],
  templateUrl: './book.component.html',
  styleUrl: './book.component.scss',
})
export class BookComponent implements OnInit {
  books: Book[] = [];
  error: String = '';
  activeBook: Book | undefined;

  constructor(
    private http: HttpClient,
    private bookService: BookService,
  ) {}

  getAllBooks() {
    this.bookService.getAll().subscribe(
      (res: Book[]) => {
        console.log(res);
        this.books = res;
      },
      (err) => {
        this.error = 'Problemi di connessione';
        console.log(err);
      },
    );
  }

  deleteBook(id: number) {
    this.bookService.delete(id).subscribe(
      () => {
        this.books = this.books.filter((book) => book.id !== id);
      },
      (err) => {
        this.error = 'Non è stato possibile eliminare il libro';
        console.log(err);
      },
    );
  }

  selectBook(book: Book) {
    this.activeBook = book;
    console.log(this.activeBook);
  }

  reset() {
    this.activeBook = undefined;
  }

  title = 'book';
  ngOnInit() {
    console.log('BookComponent initialized');
    this.getAllBooks();
  }
}

book.component.html

<div class="container" style="margin-top: 30px">
  <div class="alert alert-danger" *ngIf="this.error">{{ this.error }}</div>
  <p>--- I'm here ---</p>

  <div class="row">
    <div class="col-lg-6 col-md-8 mx-0">
      <ul class="list-group" *ngFor="let book of books">
        <li
          class="list-group-item"
          [style.backgroundColor]="
            this.activeBook?.id == book.id ? 'orange' : null
          "
        >
          {{ book.title }} - {{ book.author }}
          <img
            [src]="book.img ? book.img : './assets/img/add.png'"
            class="img-thumbnail ms-4"
            style="max-width: 100px"
          />

          <div class="pull-right">
            <span [style.color]="book.price > 15 ? 'red' : null">
              {{ book.price | currency: "EUR" }}</span
            >
            <i
              class="fa fa-info-circle"
              style="margin-left: 5px"
              aria-hidden="true"
              [routerLink]="['/book', book.id]"
            ></i>
            <i
              class="fa fa-edit"
              style="margin-left: 5px"
              (click)="selectBook(book)"
            ></i>
            <i
              class="fa fa-trash"
              style="margin-left: 5px"
              (click)="deleteBook(book.id)"
            ></i>
          </div>
        </li>
      </ul>
      <img
        [src]="this.activeBook?.img"
        class="img-fluid"
        style="max-width: 400px"
        class="me-2"
      />
    </div>
    <div class="col-lg-6 col-md-12 mx-0">
      <app-form
        [activeBook]="activeBook"
        [books]="books"
        (resetClick)="reset()"
      ></app-form>
    </div>
  </div>
</div>

app-config.ts

import { ApplicationConfig } from "@angular/core";
import { provideRouter } from "@angular/router";
import { routes } from "./app.routes";

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes)],
};

app.rooter.ts

import { Routes } from "@angular/router";
import { BookDetailComponent } from "./features/book-detail/book-detail.component";
import { BookComponent } from "./components/book/book.component";

console.log("Routes are being initialized");

export const routes: Routes = [
  { path: "", component: BookComponent },
  { path: "book", component: BookComponent },
  { path: "book/:id", component: BookDetailComponent },
  { path: "**", redirectTo: "" },
];

main.ts

/// <reference types="@angular/localize" />

import { bootstrapApplication } from "@angular/platform-browser";
import { AppComponent } from "./app/app.component";
import { HttpClientModule } from "@angular/common/http";
import { importProvidersFrom } from "@angular/core";

bootstrapApplication(AppComponent, {
  providers: [importProvidersFrom(HttpClientModule)],
}).catch((err) => console.error(err));

book-detail.components.ts

import { Component } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { CommonModule } from "@angular/common";
import { RouterModule } from "@angular/router";

@Component({
  selector: "app-book-detail",
  standalone: true,
  imports: [CommonModule, RouterModule],
  templateUrl: "./book-detail.component.html",
  styleUrls: ["./book-detail.component.scss"],
})
export class BookDetailComponent {
  constructor(private route: ActivatedRoute) {
    this.route.params.subscribe((params) => {
      console.log("Route parameters:", params);
    });
  }

  ngOnInit() {
    console.log("BookDetailComponent loaded");
  }
}

Solution

  • You are not using the app.config.ts hence this problem is happening, because you are providing the hardcoded object as the second argument to bootstrapApplication. So the provideRouter configuration is never set on bootstrapApplication and routing is not initialized, but we are using routerLink which looks ActivatedRoute DI token, but it is not present hence this problem is happening, if we initialize routing on application bootstrap the problem goes away.


    The second thing is you do not need HttpClientModule (deprecated) instead just use provideHttpClient, also you just need to import it once, in the main.ts, you can remove all the imports of HttpClientModule in your application.


    Hence you can change the code as below, where we use the app.config.ts instead:

    app-config.ts

    import { ApplicationConfig } from '@angular/core';
    import { provideRouter } from '@angular/router';
    import { routes } from './app.routes';
    import { provideHttpClient } from '@angular/common/http';
    
    export const appConfig: ApplicationConfig = {
      providers: [
        provideHttpClient(),
        provideRouter(routes),
      ],
    };
    

    main.ts

    /// <reference types="@angular/localize" />
    
    import { bootstrapApplication } from '@angular/platform-browser';
    import { AppComponent } from './app/app.component';
    import { appConfig } from './app/app.config';
    import { HttpClientModule } from '@angular/common/http';
    import { importProvidersFrom } from '@angular/core';
    
    bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));