//Service
// Service for handling Hero-related API calls
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { map, Observable, Subject } from 'rxjs';
import { Hero } from '../shared/hero';
@Injectable({
providedIn: 'root'
})
export class HeroService {
// Base URL for the API
apiUrl = 'http://localhost:5116/api/'
// HTTP options with headers
httpOptions = {
headers: new HttpHeaders({
ContentType: 'application/json'
})
}
constructor(private _httpClient: HttpClient) { }
// Method to get all heroes from the API
getHeroes(): Observable<Hero[]> {
return this._httpClient.get<Hero[]>(`${this.apiUrl}Hero/GetAllHeroes`)
.pipe(map(result => result))
}
// Method to get a specific hero by ID from the API
getHero(heroId: number) {
return this._httpClient.get(`${this.apiUrl}Hero/GetHero` + "/" + heroId)
.pipe(map(result => result))
}
}
//Home page
//HTML
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title>
Home
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
//Display skeleton loading cards if Heroes data is not available -->
<div *ngIf="!Heroes">
<ion-card>
<ion-skeleton-text style="height:200px;" animated></ion-skeleton-text>
<ion-card-header></ion-card-header>
</ion-card>
//Repeated skeleton cards for loading state -->
<ion-card>
<ion-skeleton-text style="height:200px;" animated></ion-skeleton-text>
<ion-card-header></ion-card-header>
</ion-card>
<ion-card>
<ion-skeleton-text style="height:200px;" animated></ion-skeleton-text>
<ion-card-header></ion-card-header>
</ion-card>
<ion-card>
<ion-skeleton-text style="height:200px;" animated></ion-skeleton-text>
<ion-card-header>
</ion-card-header>
</ion-card>
</div>
//Pull-to-refresh functionality -->
<ion-refresher slot="fixed" (ionRefresh)="refreshHeroes($event)">
<ion-refresher-content refreshingText="Loading Heroes..."></ion-refresher-content>
</ion-refresher>
//Display hero cards when Heroes data is available -->
<ion-card button *ngFor="let hero of (Heroes | async)" [routerLink]="['hero-detail', hero.heroId]">
<ion-img [src]="hero.imageBase64"></ion-img>
</ion-card>
</ion-content>
//TS
import { Component } from '@angular/core';
import { IonicModule, ToastController } from '@ionic/angular';
import { ExploreContainerComponent } from '../explore-container/explore-container.component';
import { Hero } from '../shared/hero';
import { HeroService } from '../services/hero.service';
import { AppModule } from '../app.module';
import { CommonModule } from '@angular/common';
import { Observable } from 'rxjs';
import { RouterLink } from '@angular/router';
@Component({
selector: 'app-tab1',
templateUrl: 'tab1.page.html',
styleUrls: ['tab1.page.scss'],
standalone: true,
imports: [IonicModule, ExploreContainerComponent, AppModule, CommonModule, RouterLink],
})
export class Tab1Page {
// Observable to hold the list of heroes
Heroes: Observable<Hero[]>;
constructor(private _toastController: ToastController, private _heroService: HeroService) {
// Fetching heroes from the service
this.Heroes = this._heroService.getHeroes();
}
ngOnInit() { }
// Method to refresh the list of heroes
refreshHeroes(event:any){
this.Heroes = this._heroService.getHeroes();
// Complete the refresh event
event.target.complete();
// Show a toast notification
const toast = this._toastController.create({
message: "Heroes are refreshed",
duration: 3000,
position: "bottom"
})
toast.then((toastMessage) => {
toastMessage.present();
})
}
}
//hero detail page
//HTML
<ion-header [translucent]="true">
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title></ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
//Display hero image -->
<ion-img src={{heroDetail?.imageBase64}}></ion-img>
//Hero details card -->
<ion-card no-margin>
<ion-card-header>
<ion-card-title>
{{heroDetail?.name}}
</ion-card-title>
</ion-card-header>
</ion-card>
<ion-card>
<ion-list lines="none">
//Display hero's age -->
<ion-item>
<ion-label>Age</ion-label>
<ion-chip color="primary">{{heroDetail?.age}}</ion-chip>
</ion-item>
//Display hero's height -->
<ion-item>
<ion-label>Height</ion-label>
<ion-chip color="secondary">{{heroDetail?.height}}</ion-chip>
</ion-item>
//Display hero's birthday -->
<ion-item>
<ion-label>Birthday</ion-label>
<ion-chip>{{heroDetail?.birthday}}</ion-chip>
</ion-item>
</ion-list>
</ion-card>
//Floating action button to open modal for hero status -->
<ion-fab vertical="top" horizontal="end" slot="fixed">
<ion-fab-button (click)="openModal(heroDetail?.isAlive)">
<ion-icon name="eye"></ion-icon>
</ion-fab-button>
</ion-fab>
</ion-content>
//TS
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule, ModalController, ToastController } from '@ionic/angular';
import { HeroService } from '../services/hero.service';
import { ActivatedRoute } from '@angular/router';
import { HerostatusPage } from '../herostatus/herostatus.page';
@Component({
selector: 'app-hero-detail',
templateUrl: './hero-detail.page.html',
styleUrls: ['./hero-detail.page.scss'],
standalone: true,
imports: [IonicModule, CommonModule, FormsModule]
})
export class HeroDetailPage implements OnInit {
// Variable to hold the hero details
heroDetail: any;
constructor(
private _toastController: ToastController,
private _heroService: HeroService,
private _modal: ModalController,
private route: ActivatedRoute
) {
// Fetch the hero details based on the hero ID from the route parameters
this._heroService.getHero(+this.route.snapshot.params['heroId']).subscribe(result => {
this.heroDetail = result
// Show a toast notification
const toast = this._toastController.create({
message: "Hero " + this.heroDetail.name + " is viewable",
duration: 3000,
position: "bottom"
})
toast.then((toastMessage) => {
toastMessage.present();
})
})
}
ngOnInit(): void { }
// Method to open a modal showing the hero's status (alive or dead)
async openModal(status: boolean) {
const statusModal = await this._modal.create({
component: HerostatusPage,
componentProps: {
value: status
}
})
return await statusModal.present()
}
}
//Hero status page
//HTML
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title>Hero Status</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-fab vertical="center" horizontal="center">
<ion-fab-button *ngIf="value">
<ion-icon name="happy-outline" *ngIf="value"> </ion-icon>
Alive
</ion-fab-button>
<ion-fab-button *ngIf="!value">
<ion-icon name="sad-outline" *ngIf="!value"></ion-icon>
Dead
</ion-fab-button>
</ion-fab>
<ion-button (click)="closeModal()" class="container" color="danger">
<ion-icon name="close-circle-outline" size="large"></ion-icon>
</ion-button>
</ion-content>
//TS
import { Component, Input, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule, ModalController } from '@ionic/angular';
@Component({
selector: 'app-herostatus',
templateUrl: './herostatus.page.html',
styleUrls: ['./herostatus.page.scss'],
standalone: true,
imports: [IonicModule, CommonModule, FormsModule]
})
export class HerostatusPage implements OnInit {
// Input property to receive the hero's status (alive or dead)
@Input() value: any;
constructor(private _modal: ModalController) { }
ngOnInit() { }
// Method to close the modal
closeModal() {
this._modal.dismiss()
}
}