Latihan 2 – Menampilkan Anime Berdasarkan Genre

Melanjutkan dari modul latihan sebelumnya, pada modul ini kita akan menampilkan list anime berdasarkan genre.

Hasil akhir dari latihan yang diharapkan adalah seperti gambar berikut.

Ada dua pendekatan, Anda bisa membuat fungsi baru untuk mengambil data anime berdasarkan genre.

Atau kita lakukan sedikit modifikasi pada fungsi getAnimes (fungsi yang menampilkan seluruh list anime).

Pada modul ini akan dibahas dengan pendekatan kedua, yaitu modifikasi fungsi getAnimes.

Solusi

Bagian Backend

Buka file cmd/api/animeModel.go, lakukan modifikasi pada fungsi All().

Modifikasi agar fungsi All menerima parameter genre_id.

func (m *AnimeModel) All(genre ...int) ([]*Anime, error) {

Lalu modifikasi query statment

	where := ""

	if len(genre) > 0 {
		where = fmt.Sprintf("WHERE id IN (SELECT anime_id FROM animes_genres WHERE genre_id= %d)", genre[0])
	}

	query := fmt.Sprintf(`SELECT id, title, description, year
						  FROM animes %s ORDER BY title`, where)

Berikut isi lengkap file cmd/api/animeModel.go

func (m *AnimeModel) All(genre ...int) ([]*Anime, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	where := ""

	if len(genre) > 0 {
		where = fmt.Sprintf("WHERE id IN (SELECT anime_id FROM animes_genres WHERE genre_id= %d)", genre[0])
	}

	query := fmt.Sprintf(`SELECT id, title, description, year
						  FROM animes %s ORDER BY title`, where)

	rows, err := m.DB.QueryContext(ctx, query)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var animes []*Anime

	for rows.Next() {
		var anime Anime
		err = rows.Scan(
			&anime.Id,
			&anime.Title,
			&anime.Desc,
			&anime.Year,
		)
		if err != nil {
			return nil, err
		}

		query2 := `SELECT ag.id, ag.anime_id, ag.genre_id, g.genre_name
		FROM animes_genres ag
		LEFT JOIN genres g  ON (g.id = ag.id)
		WHERE ag.anime_id = $1
		`

		rows2, _ := m.DB.QueryContext(ctx, query2, anime.Id)

		genre := make(map[int]string)
		for rows2.Next() {
			var ag animeGenre
			err = rows2.Scan(
				&ag.Id,
				&ag.AnimeId,
				&ag.GenreId,
				&ag.Genre.GenreName,
			)
			if err != nil {
				return nil, err
			}
			genre[ag.Id] = ag.Genre.GenreName
		}
		rows2.Close()
		anime.AnimeGenre = genre
		animes = append(animes, &anime)
	}

	return animes, nil
}

Tambahkan routing pada cmd/api/routes.go

router.HandlerFunc(http.MethodGet, "/v1/animes/:genre_id", app.getAnimeByGenre)

Tambahkan fungsi untuk handler movie berdasarkan genre pada cmd/api/animesHandlers.go

func (app *application) getAnimeByGenre(rw http.ResponseWriter, r *http.Request) {
	params := httprouter.ParamsFromContext(r.Context())

	genreid, err := strconv.Atoi(params.ByName("genre_id"))
	if err != nil {
		app.errorJSON(rw, err)
		return
	}
	animes, err := app.models.Animes.All(genreid)
	app.logger.Println(genreid)

	err = app.writeJSON(rw, http.StatusOK, animes, "animes")
	if err != nil {
		app.errorJSON(rw, err)
	}
}

Bagian Frontend

Buat component baru src/components/Genre.js isi dengan code berikut

import React, {Component, Fragment} from 'react';
import { Link } from 'react-router-dom';

export default class Genre extends Component{
    state = {
        animes : [],
        isLoaded : false,
        genreName : "",
    }

    componentDidMount(){
        fetch("http://localhost:4000/v1/animes/" + this.props.match.params.id)
        .then((response) => response.json())
        .then((json) =>{
            this.setState({
                animes: json.animes,
                isLoaded: true,
                genreName : this.props.location.genrename,
            })
            console.log(json)
        })
    }

    render(){
        let {animes, isLoaded, genreName} = this.state;
        if (!animes){
            animes = []
        }

        
        if(!isLoaded){
            return <p>Data is loading...</p>
        }else{        
            return(
                <Fragment>
                    <h2>Anime List - {genreName}</h2>
                    <ul>
                        {animes.map((e) =>(
                            <li key={e.id}>
                                <Link to={`/animes/${e.id}`}>{e.title}</Link>
                            </li>
                        ))}
                    </ul>
                </Fragment>                
            );
        }
    }
}

Pada src/App.js, tambahkan route baru pada element <Switch> untuk handle movie by genre

<Route path="/genre/:id" component={Genre} />

Dan tambahkan import component yang digunakan.

import Genre from './components/Genre';

Berikut isi lengkap src/App.js

import React from 'react';
import {BrowserRouter as Router, Switch, Route, Link} from 'react-router-dom';
import Home from './components/Home';
import Animes from './components/Animes';
import Admin from './components/Admin';
import Genres from './components/Genres';
import Anime from './components/Anime';
import Genre from './components/Genre';

export default function App() {
  return (
  <Router>
    <div className="container">
      <div className="row">
          <h1 className="mt-3">Anime Collection</h1>
          <hr className="mb-3"/>
      </div>
      <div className="row">
          <div className="col-md-2">
              <nav>
                  <ul className="list-group">
                      <li className="list-group-item"><Link to="/">Home</Link></li>
                      <li className="list-group-item"><Link to="/animes">Anime</Link></li>
                      <li className="list-group-item"><Link to="/genres">Genre</Link></li>
                      <li className="list-group-item"><Link to="/admin">Admin</Link></li>
                  </ul>
              </nav>
          </div>
          <div className="col-md-10">
            <Switch>
              <Route path="/animes/:id" component={Anime} />
              <Route path="/animes"><Animes /></Route>
              <Route path="/genre/:id" component={Genre} />
              <Route exact path="/genres"><Genres /></Route>
              <Route path="/admin"><Admin /></Route>
              <Route path="/"><Home /></Route>
            </Switch>          
          </div>
      </div>
    </div>    
  </Router>
  );
}
Sharing is caring:

Leave a Comment