Hero - ionic II L9

PHOTO EMBED

Tue Jun 18 2024 11:43:37 GMT+0000 (Coordinated Universal Time)

Saved by @iamkatmakhafola

//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()
  }
}
content_copyCOPY