Búsqueda de sitios web

angular - Problema con [routerLink] NullInjectorError: No hay proveedor para ActivatedRoute


Estoy haciendo mi primera aplicación web, después de tomar un curso. Me he quedado atascado en las Rutas, y aunque he hecho varios cambios leyendo otros posts, preguntando a Chatgpt o Copilot, no puedo resolver el problema, que ocurre cuando agrego un [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)

si en el app.components.ts borro en medio de mi página html no veo nada, así que creo que la estructura correcta que usé es

si elimino el [routerLink] del html todo funciona correctamente, por supuesto no puedo ir al otro componente

si en su lugar pongo [routerLink] tengo el texto ‘Estoy aquí’ que está antes de ngFor y el formulario en

pero no tengo la lista

cuando lanzo el servidor entro correctamente en el ngOnInit de book.components.ts

Accedo correctamente al método getAllBooks sin ningún problema ni error

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

versión de Angular 18.2.11

Añadiré todo el código principal

app.componentes.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-libro ComponenteLibro

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>

configuración-app.ts

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

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

app.enrutador.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: "" },
];

principal.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));

detalle-del-libro.componentes.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");
  }
}

Respuesta #1

No estás utilizando el app.config.ts, por lo tanto, ocurre este problema, porque estás proporcionando el objeto codificado directamente como segundo argumento de bootstrapApplication. Así que la configuración de provideRouter nunca se establece en bootstrapApplication y el enrutamiento no se inicializa, pero estamos usando routerLink que busca el token de DI ActivatedRoute, pero no está presente, por lo que ocurre este problema. Si inicializamos el enrutamiento en el arranque de la aplicación, el problema desaparece.


Lo segundo es que no necesitas HttpClientModule (obsoleto), en su lugar solo usa provideHttpClient, además solo necesitas importarlo una vez, en el main.ts, puedes eliminar todas las importaciones de HttpClientModule en tu aplicación.


Por lo tanto, puedes cambiar el código como se indica a continuación, donde usamos el app.config.ts en su lugar:

configuración-app.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),
  ],
};

principal.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));