Front End

8 abr, 2019

Passando objetos na navegação com Angular

Publicidade

Olá!

Neste artigo apresento duas abordagens para passar objetos na navegação de um componente para outro utilizando Angular 7.

O cenário proposto é o consumo de uma API de animes. Mais detalhes em https://kitsu.io.

A tela principal, que chamei de mestre, traz uma relação de animes com suas propriedades apresentadas em cards.

A ideia aqui é navegar até um componente (detalhes) que apresente mais características do anime selecionado, passando um objeto complexo com vários atributos.

Se preferir, assista o vídeo do conteúdo deste artigo:

Criei dois componentes. No terminal eu fiz:

ng g c mestre
ng g c detalhes

Solicitei ainda dois services.

ng g service api
ng g service data

O primeiro service é responsável pela lógica de acesso à API.

@Injectable({
providedIn: ‘root’
})
export class ApiService {
url: string = ‘https://kitsu.io/api/edge/anime?page[limit]=10'
constructor(private http: HttpClient) { }
getData() {
return this.http.get<Anime[]>(this.url)
}

Declarei uma variável que recebe o endpoint da API. Coloquei o HttpClient no construtor e no método getData() solicito o retorno através do get, passando a url.

Defini uma interface Anime para mapear alguns campos da resposta.

export interface Anime {
attributes: Attributes;
id: string;
}
export interface Attributes {
canonicalTitle: string;
synopsis: string;
episodeCount: number;
startDate: Date;
endDate: Date;
posterImage: PosterImage;
}
export interface PosterImage {
tiny: string;
small: string;
medium: string;
large: string;
original: string;
}

anime.interface.ts

No arquivo app-routing.module.ts eu inclui no array de rotas três caminhos:

const routes: Routes = [
{ path: '', redirectTo: '/mestre', pathMatch: 'full' },
{ path: 'mestre', component: MestreComponent },
{ path: 'detalhes', component: DetalhesComponent },
];

O primeiro path indica para iniciar no caminho mestre, associado ao componente MestreComponent, segundo path. O último é uma rota para detalhes.

O arquivo app.component.html só contém a diretiva router-outlet.

<router-outlet></router-outlet>

No template html eu apresento o resultado da consulta, realizando um iteração na lista de objetos anime com *ngFor, conforme trecho do códgo abaixo:

<div *ngFor="let anime of list" class="card col-lg-4" style="width: 18rem;">
<img [src]="anime?.attributes?.posterImage?.small " class="card-img-top img-thumbnail shadow"
alt="">
<div class="card-body">
<h5 class="card-title">{{anime?.attributes?.canonicalTitle}}</h5>
<p class="card-text">{{anime?.attributes?.synopsis.slice(0,50)}}</p>
<button class="btn btn-primary" (click)="goToDetalhesByService(anime)">Detalhes (service)</button>
<button class="btn btn-primary" (click)="goToDetalhesByState(anime)">Detalhes (state)</button>
</div>
</div>

Note que tenho dois botões, cada um com um event binding representando uma abordagem diferente (o código completo está no Git, não se preocupe).

Na classe MestreComponent (mestre.component.ts) tenho um array de animes e uma injeção no construtor dos dois services criados e a classe responsável pela navegação no Angular.

list: Anime[]
constructor(private api: ApiService, private dataService: DataService, private router: Router) {}
ngOnInit(): void {
this.api.getData()
.subscribe((response: any) => {
this.list = response.data;
console.log(this.list)
})
}

No método ngOnInit() invocamos o método da api atribuindo o resultado à lista de animes.

1ª abordagem – Service

No método goToDetalhesByService(anime:Anime), antes de navegar até o componente detalhes eu seto o objeto anime no service Data.

goToDetalhesByService(anime: Anime) {
this.dataService.setAnime(anime);
this.router.navigateByUrl(‘/detalhes’)
}

A implementação da classe DataService (data.service.ts) é composta de um objeto anime e um get() e set() desse objeto.

@Injectable({
providedIn: 'root'
})
export class DataService {
private anime: Anime;
constructor() { }

setAnime(anime: Anime) {
this.anime = anime;
}
getAnime() {
return this.anime;
}
}

2ª Abordagem –  Status

Essa abordagem utiliza um recurso recente,o state, disponível na versão 7.2.

Passamos como parâmetro da navegação um propriedade status o objeto anime selecionado.

goToDetalhesByState(anime: Anime) {
this.router.navigateByUrl('/detalhes', {
state: { anime: anime }
})
}

No componente Detalhes recupero o anime. Lembre -se de que o cenário é um exemplo – eu recupero o objeto prevendo as duas possibilidades.

Dessa forma, temos na classe DetalhesComponent (detalhes.component.ts) o seguinte código:

//trecho da classe DetalhesComponent
anime: Anime;
constructor(private dataService: DataService, private router: Router) {
const nav = this.router.getCurrentNavigation();
this.anime = nav.extras.state.anime;
}
ngOnInit(): void {
if (!this.anime){
this.anime = this.dataService.getAnime()
}
}

No método construtor, declaro uma variável que recupera a navegação e logo em seguida atribui ao objeto anime.

Para abordagem que utiliza um service, no método ngOnInit() verifico se existe. Caso contrário, recupero do service através do método getAnime().

Bem tranquilo!

Há outras abordagens, mas preferi apresentar essas duas.

Se você leu até aqui, obrigado!

Até!