【Angular】サバイバルTypeScriptの猫画像ジェネレータを実装するよ

Angular CLI: 16.1.5

Angular: 16.1.6

[前回](https://qiita.com/SZR/items/a7a5145fea474663dce8)
の続きです。
業務で人生初のフロントエンド開発をAngularで行うことになりAngularのチュートリアルを一通り実施後、TypeScript未経験なこともありそちらの学習も兼ねて、サバイバルTypeScriptを敢えてAngularで実施しました

フォルダ構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2728856/ce9c31c5-35fd-57b3-37b3-98d25d8c68aa.png)


```TypeScript:app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CatImgComponent } from './component/cat-img/cat-img.component';
import { HttpClientModule } from "@angular/common/http";
import { CatService } from './service/catService';

@NgModule({
  declarations: [
    AppComponent,
    CatImgComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [CatService],
  bootstrap: [AppComponent]
})
export class AppModule { }

```
```TypeScript:app.component.html
<app-cat-img></app-cat-img>
```
```TypeScript:catService.ts
import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { catchError } from "rxjs/operators";
import { throwError, Observable } from "rxjs";
import { Catmodel } from "../model/catModel";

@Injectable({
    providedIn: 'root'
})

export class CatService{
    constructor(private http: HttpClient) {}

    // The Cat  APIを呼び出し猫画像URLを取得
    getCatImgService(): Observable<Catmodel>{
            const apiUrl = 'https://api.thecatapi.com/v1/images/search';
            return this.http.get<Catmodel>(apiUrl).pipe(
                catchError(error => {
                    console.error('画像の取得に失敗しました。', error);
                return throwError('画像の取得に失敗しました。');
                })
            );    
    }
}
```
```TypeScript:catModel.ts
// The Cat API呼び出し時に返ってくる値を入れる変数を定義

export class Catmodel {

    id?: string;
    url?: string;
    width?: number;
    height?: number;

}

```
```TypeScript:cat-img-component.ts
import { Component, OnInit } from '@angular/core';
import { Catmodel } from 'src/app/model/catModel';
import { HttpClient } from '@angular/common/http';
import { CatService } from 'src/app/service/catService';

@Component({
  selector: 'app-cat-img',
  templateUrl: './cat-img.component.html',
  styleUrls: ['./cat-img.component.css']
})
export class CatImgComponent implements OnInit{

  catdata? : Catmodel;
  error?: string;
  cat?: any;

  constructor(private catService: CatService, private http: HttpClient) { }

  // 画面を表示した際にgetCatImgを呼び出し猫を表示する
  ngOnInit() {
    this.getCatImg();
  }
  
  // 更新ボタンが押された際にロード画像を表示し、getCatImgを呼び出す
  onClick(){
    this.catdata = undefined;
    this.getCatImg();
  }
  // サービス側でThe Cat APIを呼び出し取得した猫画像URLをcatdataに格納
  getCatImg(){
    this.catService.getCatImgService().subscribe(
      (data: any) => {
        this.catdata = data[0];
        console.log(this.catdata);
      },
      (error) => {
        this.error = '画像の取得に失敗しました。';
      }
    );
  }
}

```
```TypeScript:cat-img-component.html
<div class="mainBodyBlock" >
    <p class="titleText">サービスから猫の画像をランダムに表示</p>
    <span class="Button" (click)="onClick()">更新</span>
        <div class="imgBodyBlock" >
            <ng-container *ngIf="catdata"; else="elseBlock">
                <img [src]="catdata.url" alt="Cat" width="450px" height="300px">
            </ng-container>
            <ng-template #elseBlock>
                <p>{{ error }}</p>
            </ng-template>
        </div>
</div>

```
```TypeScript:cat-img-component.css

.Button {
    background-color: #FF6B6B;
    color: white;
    padding: 1rem 2rem;
    border-radius: 0.4rem;
    cursor: pointer;
    text-align: center;
    margin: 0 auto;
    display: block;
    font-size: 1.2rem;
    font-weight: bold;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    transition: background-color 0.3s, transform 0.2s, box-shadow 0.3s;
}

.Button:hover {
    background-color: #FF4040;
    transform: scale(1.05);
    box-shadow: 0 6px 10px rgba(0, 0, 0, 0.2);
}

.Button:active {
    background-color: #FF4040;
    transform: translateY(2px);
    box-shadow: none;
}

@keyframes shake {
    0% { transform: translateX(0); }
    25% { transform: translateX(-5px) rotate(5deg); }
    50% { transform: translateX(5px) rotate(-5deg); }
    75% { transform: translateX(-5px) rotate(5deg); }
    100% { transform: translateX(0); }
}
.mainBodyBlock {
    width: 500px;
    height: 600px;
    margin: auto;
    padding: 30px;
    background-color: #E5E9F2;
    border-radius: 10px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.titleText {
    text-align: center;
    color: #3498DB;
    font-size: 1.5rem;
    font-weight: bold;
    margin-bottom: 20px;
}

.imgBodyBlock {
    background-color: #FFFFFF;
    width: 450px;
    height: 300px;
    margin-top: 50px;
    margin-left: auto;
    margin-right: auto;
    background-image: url("../../../assets/GalaxyNeko.jpg");
    background-repeat: no-repeat;
    background-size: cover;
    border-radius: 10px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.imgBodyBlock img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 10px;
}

```
画像が読み込まれるまではロード画面としてGalaxyNeko.jpgを表示する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2728856/1f546e44-c318-3abb-3a6f-5b946efd1197.png)


猫画像が読み込まれた状態です。
かわいい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2728856/6ae0fdc0-97bc-65af-5351-220128ced463.png)


.subscribe は非推奨らしいのでasyncパイプを使用する記述に変更する

```TypeScript:cat-img-component.ts
import { Component } from '@angular/core';
import { Catmodel } from 'src/app/model/catModel';
import { CatService } from 'src/app/service/catService';

@Component({
  selector: 'app-cat-img',
  templateUrl: './cat-img.component.html',
  styleUrls: ['./cat-img.component.css']
})
export class CatImgComponent {

  catdata$!: Observable<Catmodel>;
  error?: string;

  constructor(private catService: CatService) { }

  onClick() {
    this.getCatImg();
  }

  getCatImg() {
    this.catdata$ = this.catService.getCatImgService().pipe(
      catchError(error => {
        this.error = '画像の取得に失敗しました。';
        return throwError("画像の取得に失敗しました。");
      })
    );
  }
}

```

```TypeScript:cat-img-component.html
<div class="mainBodyBlock">
  <p class="titleText">猫の画像をランダムに表示</p>
  <span class="Button" (click)="onClick()">click</span>
  <div class="imgBodyBlock">
    <ng-container *ngIf="catdata$ | async as catdata; else elseBlock">
      <img class="catImg" [src]="catdata.url" alt="Cat">
    </ng-container>
    <ng-template #elseBlock>
      <p>{{ error }}</p>
    </ng-template>
  </div>
</div>

```



# andmore...

他にもこんな記事書いてます。

https://qiita.com/SZR/items/837574ffe887a7556fa6

https://qiita.com/SZR/items/3e3a02e6c316438fe5a3

https://qiita.com/SZR/items/1c8a7be4267071f6c295

https://qiita.com/SZR/items/a000cbaa583dcd2a344b

https://qiita.com/SZR/items/1d8edaf95fa2f5bbe821

https://qiita.com/SZR/items/1debe21a6759d0067ad7