
Poke api es un servicio abierto al publico el cual aprovechamos para hacer el modelo inicial de una pokedex con diferentes componentes de angular material como mat-table, mat-paginator, mat-filter, mat-snackbar, mat-cards, mat-chips.
Tabla de Contenido
Overview

Si quieres mas información de pokeapi, en el siguiente link.
Requerimientos
- Conocimientos básicos de Angular (crear un proyecto, crear componentes)
- Conocimientos muy básicos de Angular Material
- Creación del proyecto en Angular con hoja de estilos SCSS
- Proyecto con dependencia de Angular material añadida
Creación inicial de componentes
ng g c components/header ng g c components/footer ng g c components/poke-detail ng g c components/poke-table
- poke-detail : componente de detalle del pokemon seleccionado
- poke-table: componente de tabla del pokemon
Creación inicial del servicio
ng g s services/pokemon
Servicio en el cual se consume el api de pokeapi
Creación de archivo compartido styles
En la carpeta de assets creamos dos carpetas y en styles creamos un archivo para almacenar los colores que vamos a utilizar en la aplicación:
- assets/images
- assets/styles/variables.scss
$red: #bd3736; $black: black; $white: #fcfcfc; $yellow: #FFD924;
Deberíamos tener la siguiente estructura

Modulo compartido componentes material
En shared creamos un modulo que va a ser compartido y carga los componentes necesarios, este modulo se debe colocar en los imports de app.module.ts
import { NgModule } from '@angular/core'; import { MatCardModule } from '@angular/material/card'; import { MatButtonModule } from '@angular/material/button'; import { MatMenuModule } from '@angular/material/menu'; import { MatToolbarModule } from '@angular/material/toolbar'; import { MatIconModule } from '@angular/material/icon'; import {MatGridListModule} from '@angular/material/grid-list'; import {MatTableModule} from '@angular/material/table'; import {MatPaginatorModule} from '@angular/material/paginator'; import {MatFormFieldModule} from '@angular/material/form-field'; import {MatInputModule} from '@angular/material/input'; import {MatChipsModule} from '@angular/material/chips'; @NgModule({ imports: [ MatButtonModule, MatMenuModule, MatToolbarModule, MatIconModule, MatCardModule, MatGridListModule, MatTableModule, MatPaginatorModule, MatFormFieldModule, MatInputModule, MatChipsModule ], exports: [ MatButtonModule, MatMenuModule, MatToolbarModule, MatIconModule, MatCardModule, MatGridListModule, MatTableModule, MatPaginatorModule, MatFormFieldModule, MatInputModule, MatChipsModule ] }) export class MaterialModule {}
app.module.ts
Importamos HttpClientModule para poder realizar peticiones HTTP
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { MaterialModule } from 'src/shared/material.module'; import { HeaderComponent } from './components/header/header.component'; import { PokeTableComponent } from './components/poke-table/poke-table.component'; import { PokeDetailComponent } from './components/poke-detail/poke-detail.component'; import { HttpClientModule } from '@angular/common/http'; import { FooterComponent } from './components/footer/footer.component'; @NgModule({ declarations: [ AppComponent, HeaderComponent, PokeTableComponent, PokeDetailComponent, FooterComponent ], imports: [ BrowserModule, AppRoutingModule, BrowserAnimationsModule, HttpClientModule, MaterialModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
app.component.html
<app-header></app-header> <router-outlet></router-outlet> <app-footer></app-footer>
app-routing.module.ts
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { PokeDetailComponent } from './components/poke-detail/poke-detail.component'; import { PokeTableComponent } from './components/poke-table/poke-table.component'; const routes: Routes = [ {path: 'home', component: PokeTableComponent}, {path: 'pokeDetail/:id', component: PokeDetailComponent}, {path: '', pathMatch: 'full', redirectTo: 'home' }, {path: '**', pathMatch: 'full', redirectTo: 'home'}, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
environment.ts
Creamos la url base de pokeapi en enviroment.ts
export const environment = { production: false, baseUrl: 'https://pokeapi.co/api/v2' };
pokemon.service.ts
Si llamamos el servicio unicamente con /pokemon, trae los nombres y una url de la información del pokemon, nosotros utilizaremos /pokemon/${index} donde index va a ser el pokemon que deseemos, cabe resaltar que vamos a traer los 150 primer pokemon de la primera generación
import { Injectable } from '@angular/core'; import { environment } from 'src/environments/environment'; import { HttpClient } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class PokemonService { baseUrl: string = environment.baseUrl; constructor(private http: HttpClient) { } //Obtiene pokemon getPokemons(index){ return this.http.get<any>(`${this.baseUrl}/pokemon/${index}`); } }
Poke-table
En el componente poke-table vamos a listar los pokemons de la siguiente manera
import { Component, OnInit, ViewChild } from '@angular/core'; import { PokemonService } from 'src/app/services/pokemon.service'; import { MatPaginator } from '@angular/material/paginator'; import { MatTableDataSource } from '@angular/material/table'; import { Router } from '@angular/router'; @Component({ selector: 'app-poke-table', templateUrl: './poke-table.component.html', styleUrls: ['./poke-table.component.scss'] }) export class PokeTableComponent implements OnInit { //Columnas que se muestran de la tabla de angular material displayedColumns: string[] = ['position', 'image', 'name']; data: any[] = []; dataSource = new MatTableDataSource<any>(this.data); @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; pokemons = []; constructor(private pokemonService: PokemonService, private router: Router) { } ngOnInit(): void { this.getPokemons(); } getPokemons() { let pokemonData; for (let i = 1; i <= 150; i++) { this.pokemonService.getPokemons(i).subscribe( res => { pokemonData = { position: i, image: res.sprites.front_default, name: res.name }; //ponemos la data que viene del servicio en un arreglo this.data.push(pokemonData); this.dataSource = new MatTableDataSource<any>(this.data); this.dataSource.paginator = this.paginator; }, err => { console.log(err); } ); } } //Filtro para el paginador applyFilter(event: Event) { const filterValue = (event.target as HTMLInputElement).value; this.dataSource.filter = filterValue.trim().toLowerCase(); if (this.dataSource.paginator) { this.dataSource.paginator.firstPage(); } } //Obtiene elemento seleccionado getRow(row){ //console.log(row); this.router.navigateByUrl(`/pokeDetail/${row.position}`) } }
<div class="container"> <mat-form-field> <mat-label>Filter</mat-label> <input matInput (keyup)="applyFilter($event)" #input> </mat-form-field> <table mat-table [dataSource]="dataSource"> <ng-container matColumnDef="position"> <th mat-header-cell *matHeaderCellDef> No. </th> <td mat-cell *matCellDef="let element"> {{element.position}} </td> </ng-container> <ng-container matColumnDef="image"> <th mat-header-cell *matHeaderCellDef> Image </th> <td mat-cell *matCellDef="let element"> <img src="{{element.image}}" alt="pokemon" width="100" height="100"> </td> </ng-container> <ng-container matColumnDef="name"> <th mat-header-cell *matHeaderCellDef> Name </th> <td mat-cell *matCellDef="let element"> {{element.name}} </td> </ng-container> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="getRow(row)"></tr> <!-- Row shown when there is no matching data. --> <tr class="mat-row" *matNoDataRow> <td class="mat-cell" colspan="4">No data matching the filter "{{input.value}}"</td> </tr> </table> <mat-paginator [pageSizeOptions]="[5, 10, 20]" showFirstLastButtons></mat-paginator> </div>
@import "../../../assets/styles/variables.scss"; .container{ width: 100%; margin-top: 20px; text-align: center; } table { min-width: 80%; margin: auto; text-align: center; } .mat-header-cell { color: black; font-weight: bold; text-align: center; font-size: 15px; } //even //Representa elementos cuya posición numérica en una serie de hermanos es par: 2, 4, 6, etc. .mat-row:nth-child(even) { background-color: $white; } //odd //Representa elementos cuya posición numérica en una serie de hermanos es impar: 1, 3, 5, etc. .mat-row:nth-child(odd) { background-color: $red; & .mat-cell { color: $white; } } .mat-cell:first-letter { text-transform: uppercase; } .mat-row:hover { cursor: pointer; box-shadow: inset 0 0 0 5px $yellow; } ::ng-deep.mat-paginator { width: 80%; margin: auto; } .mat-form-field { font-size: 13px; width: 20%; margin-bottom: 5px; padding: 10px; } ::ng-deep.mat-form-field-label { color: $white !important; } ::ng-deep.mat-form-field-underline { background-color: $white !important; } input { color: $white !important; }
Poke-detail
import { Component, OnInit } from '@angular/core'; import { PokemonService } from 'src/app/services/pokemon.service'; import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'app-poke-detail', templateUrl: './poke-detail.component.html', styleUrls: ['./poke-detail.component.scss'] }) export class PokeDetailComponent implements OnInit { pokemon: any = ''; pokemonImg = ''; pokemonType = []; constructor(private activatedRouter: ActivatedRoute, private pokemonService: PokemonService) { //obtiene parametro de la url this.activatedRouter.params.subscribe( params => { this.getPokemon(params['id']); } ) } ngOnInit(): void { } getPokemon(id) { this.pokemonService.getPokemons(id).subscribe( res => { console.log(res); this.pokemon = res; this.pokemonImg = this.pokemon.sprites.front_default; this.pokemonType = res.types[0].type.name; }, err => { console.log(err); } ) } }
<mat-card class="card"> <mat-card-header> <img mat-card-avatar class="header-image" src="{{pokemonImg}}"> <mat-card-title>{{pokemon.name}}</mat-card-title> <mat-card-subtitle>Nº {{pokemon.id}}</mat-card-subtitle> </mat-card-header> <img mat-card-image src="{{pokemonImg}}" alt="pokemon"> <mat-card-content> <mat-chip-list> <mat-chip color="primary">Type: {{pokemonType}}</mat-chip> <mat-chip color="primary">Height: {{pokemon.height}}</mat-chip> <mat-chip color="primary">Weight: {{pokemon.weight}}</mat-chip> </mat-chip-list> </mat-card-content> </mat-card>
@import "../../../assets/styles/variables.scss"; .card { width: 300px; margin: auto; margin-top: 20px; background-color: $red; } .header-image { background-size: cover; } .mat-card-header{ background-color: $white; padding-top: 10px; border-radius: 10px; } .mat-card-title{ text-transform: uppercase; color: $black; } .mat-card-subtitle{ color: $black; }
Header
<mat-toolbar color="primary"> <mat-toolbar-row> <!-- Imagen que quieres en el header--> <img src="../../../assets/images/pokedex.png" alt="pokedex" width="40" height="40" (click)="home()"> </mat-toolbar-row> </mat-toolbar>
img:hover{ cursor: pointer; }
Footer
footer{ display: flex; flex: 0; justify-content: center; align-items: center; height: 80px; color: white; }
Resultado



Gracias a que se utiliza Angular Material y los componentes al tener flex la aplicación actual responsive
Conclusión
Pokeapi tiene una estructura un poco complejo de entender, en este ejercicio se trabajo sin orientación a objetos, pero lo mas recomendado es trabajar con este paradigma para así evitar muchos inconvenientes en el trabajo con esta API, en cuanto a Angular material se observa que gracias a ::ng-deep es muy sencillo cambiar los estilos de cualquier componente que deseemos.
Repositorios
Da clic en el siguiente enlace o el icono que se encuentra abajo
One Reply to “Trabajando con Poke Api para hacer una Pokedex con Angular Material”