Ir al contenido

Tutorial: Ver el detalle de un artista con sus álbumes

Al terminar este tutorial vas a ser capaz de:

  • Agregar un nuevo signal a un servicio que ya existe, para que varios componentes compartan “cuál es el elemento seleccionado”.
  • Incluir un componente como hijo directo de otro (ArtistDetail dentro de ArtistList), sin dejar de leer la selección desde el servicio compartido.
  • Iterar sobre una colección anidada (artista.albumes) con @for, el mismo patrón que ya usaste para el listado principal.

Prerrequisitos: haber completado el tutorial de compartir datos entre componentes con un servicio — ya tienes ArtistaService con artistasResource y crear(), y tu componente de listado inyectándolo.


ArtistaService ya centraliza la lectura (artistasResource) y la escritura (crear()) de tu módulo. Ahora también va a centralizar cuál artista está seleccionado:

artista.service.ts
import { Injectable, inject, signal } from '@angular/core';
import { httpResource, HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Artista, NuevoArtista } from './artista.model';
import { environment } from '../environments/environment';
@Injectable({ providedIn: 'root' })
export class ArtistaService {
private http: HttpClient = inject(HttpClient);
artistasResource = httpResource<Artista[]>(() => environment.ARTISTAS_URL);
seleccionado = signal<Artista | null>(null);
crear(nuevoArtista: NuevoArtista): Observable<Artista> {
return this.http.post<Artista>(environment.ARTISTAS_URL, nuevoArtista);
}
}

3. ArtistList selecciona un artista al hacer clic

Sección titulada «3. ArtistList selecciona un artista al hacer clic»

Agrega un (click) a cada card del listado:

artist-list.component.html
@for (artista of artistaService.artistasResource.value() ?? []; track artista.id) {
<div class="card" (click)="seleccionar(artista)">
{{ artista.nombre }}
</div>
}

Y el método correspondiente en la clase:

artist-list.component.ts
seleccionar(artista: Artista): void {
this.artistaService.seleccionado.set(artista);
}

Ventana de terminal
ng generate component artist-detail

Inyecta el mismo servicio y muestra el artista seleccionado junto con sus álbumes:

artist-detail.component.ts
import { Component, inject } from '@angular/core';
import { ArtistaService } from '../artista.service';
@Component({
selector: 'app-artist-detail',
standalone: true,
templateUrl: './artist-detail.component.html',
})
export class ArtistDetailComponent {
artistaService = inject(ArtistaService);
cerrar(): void {
this.artistaService.seleccionado.set(null);
}
}
artist-detail.component.html
@if (artistaService.seleccionado(); as artista) {
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">{{ artista.nombre }}</h5>
<p class="text-muted">{{ artista.paisOrigen }}</p>
<p>{{ artista.biografia }}</p>
<h6>Álbumes</h6>
<ul>
@for (album of artista.albumes; track album.id) {
<li>{{ album.titulo }} ({{ album.anioLanzamiento }})</li>
}
</ul>
<button type="button" (click)="cerrar()">Cerrar</button>
</div>
</div>
}

A diferencia de ArtistCreate y ArtistList (que son hermanos, sin relación entre sí), aquí sí hay una relación directa: el listado es quien abre el detalle. Por eso ArtistDetail va dentro del template de ArtistList, no en app.html:

artist-list.component.ts
import { Component, inject } from '@angular/core';
import { ArtistaService } from '../artista.service';
import { Artista } from '../artista.model';
import { ArtistDetailComponent } from '../artist-detail/artist-detail.component';
@Component({
selector: 'app-artist-list',
standalone: true,
imports: [ArtistDetailComponent],
templateUrl: './artist-list.component.html',
})
export class ArtistListComponent {
artistaService = inject(ArtistaService);
seleccionar(artista: Artista): void {
this.artistaService.seleccionado.set(artista);
}
}
artist-list.component.html
<app-artist-detail />
@for (artista of artistaService.artistasResource.value() ?? []; track artista.id) {
<div class="card" (click)="seleccionar(artista)">
{{ artista.nombre }}
</div>
}

Corre ng serve. Al hacer clic en un card:

  • Arriba del listado debe aparecer el detalle del artista, con sus álbumes.
  • “Cerrar” debe ocultarlo.
  • Hacer clic en otro card debe cambiar el detalle directamente, sin pasar por “Cerrar” primero.

Al final de este tutorial tienes:

  • ArtistaService con un tercer signal, seleccionado, además de artistasResource y crear().
  • ArtistList seleccionando un artista al hacer clic en su card.
  • ArtistDetail, hijo de ArtistList, mostrando el artista seleccionado y sus álbumes — sin @Input, leyendo el mismo servicio compartido que el resto de la app.