こんにちは、KOUKIです。
GolangとReactでMovie Appの開発をしています。
前回に引き続き、Movie Formの処理を実装します。
尚、Udemyの「Working with React and Go (Golang)」を参考にしているので、よかったら受講してみてください。
<目次>
前回
Movieデータの保存や更新を行うMovie Formの実装を行いました。
事前準備
モジュール
1 2 |
cd go-movies/ npm install react-confirm-alert --save |
削除処理
FormからMovieデータを削除できるように機能を追加します。
Cancelボタン
CancelボタンをEditMovieコンポーネントに追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// components/EditMovie.tsx ... import { Link } from 'react-router-dom' const EditMovie = (props: any) => { ... return ( <Fragment> ... <button className="btn btn-primary" type="submit">Save</button> <Link to="/admin" className="btn btn-warning ms-1">Cancel</Link> </form> </Fragment> ) } |
LinkでCancelボタンを追加しました。これで、管理画面に遷移できます。

Deleteボタンの設置
続いて、Deleteボタンを設置します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
// components/EditMovie.tsx import { Fragment, useEffect, useState, SyntheticEvent } from "react" ... const EditMovie = (props: any) => { ... const confirmDelete = (e: SyntheticEvent) => { e.preventDefault() console.log("wourld delete movie id ", id) } ... return ( <Fragment> ... <Link to="/admin" className="btn btn-warning ms-1">Cancel</Link> {id > 0 && ( <a href="#!" onClick={confirmDelete} className="btn btn-danger ms-1"> Delete </a> )} ... </Fragment> ) } export default EditMovie |
URLにidが渡された場合は、Deleteボタンを表示します。
「http://localhost:3000/admin/movie/0」

「http://localhost:3000/admin/movie/1」

削除アラートの実装
react-confirm-alertを併用しつつ、削除アラートを実装しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// components/EditMovie.tsx ... import { confirmAlert } from 'react-confirm-alert' import 'react-confirm-alert/src/react-confirm-alert.css' const EditMovie = (props: any) => ... const confirmDelete = (e: SyntheticEvent) => { e.preventDefault() console.log("wourld delete movie id ", id) confirmAlert({ title: 'Delete Movie?', message: 'Are you sure?', buttons: [ { label: 'Yes', onClick: () => console.log("Delete Movie!") }, { label: 'No', onClick: () => {} } ] }); } ... } |
削除処理 API(golang)
Golangで削除処理APIを実装します。
まずは、DBへDELETE文を発行し、データを削除する処理です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// models/movies-db.go ... func (m *DBModel) DeleteMovie(id int) error { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() stmt := "delete from movies where id = $1" _, err := m.DB.ExecContext(ctx, stmt, id) if err != nil { return err } return nil } |
続いて、ハンドラーを実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// cmd/api/movie-handlers.go func (app *application) deleteMovie(w http.ResponseWriter, r *http.Request) { params := httprouter.ParamsFromContext(r.Context()) id, err := strconv.Atoi(params.ByName("id")) if err != nil { app.errorJSON(w, err) return } err = app.models.DB.DeleteMovie(id) if err != nil { app.errorJSON(w, err) return } ok := jsonResp{ OK: true, } err = app.writeJSON(w, http.StatusOK, ok, "reponse") if err != nil { app.errorJSON(w, err) return } } |
最後に、ルートを追加します。
1 2 3 4 5 6 7 8 |
// cmd/api/routes.go ... func (app *application) routes() http.Handler { ... router.HandlerFunc(http.MethodGet, "/v1/admin/deletemovie/:id", app.deleteMovie) return app.enableCORS(router) } |
削除処理(React)
React側で削除処理を完成させましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
// components/EditMovie.ts const confirmDelete = (e: SyntheticEvent) => { e.preventDefault() confirmAlert({ title: 'Delete Movie?', message: 'Are you sure?', buttons: [ { label: 'Yes', onClick: async () => { await axios.get(`admin/deletemovie/${id}`) .then((response) => { props.history.push({ pathname: "/admin" }) }) .catch((err) => { setError(err.message) setAlert({ type: 'alert-danger', message: err.response.data.error.message }) }) } }, { label: 'No', onClick: () => {} } ] }); } |
先ほど実装した削除APIへ向けてリクエストを送る処理を追加しました。
検証1
検証のため「http://localhost:3000/admin/movie/1」のデータを削除したいと思います。

「pq: update or delete on table “movies” violates foreign key constraint “fk_movie_genries_movie_id” on table “movies_genres”」エラーが出てしまいましたね。。
idが1のデータは、movies_genresと関連を持っているので削除できません。

id2のデータで試してみましょう。
OKですね!
Admin画面の作成
削除処理とは関係ないのですが、Admin画面の作成についても触れておきます。
現状は、以下のようになっています。

Admin画面の実装
これまで実装してきた内容とほぼ同じなので、さっくりいきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
// components/Admin.tsx import { Fragment, useEffect, useState } from "react" import { Movie } from '../models/movie' import { Link } from 'react-router-dom' import axios from 'axios' const Admin = () => { const [movies, setMovies] = useState<Movie[]>([]) const [isLoaded, setIsLoaded] = useState(false) const [error, setError] = useState("") useEffect(() => { ( async () => { await axios.get('movies') .then((response) => { setMovies(response.data.movies) setIsLoaded(true) }) .catch((err) => { setError(err.message) }) } )() }, []) if (error) { return ( <div>Error: {error}</div> ) } else if (!isLoaded) { return ( <p>Loading...</p> ) } return ( <Fragment> <h2>Manage Catalogue</h2> <hr /> <div className="list-group"> {movies.map((m) => { return ( <Link key={m.id} to={`/admin/movie/${m.id}`} className="list-group-item list-group-item-action" > {m.title} </Link> ) })} </div> </Fragment> ) } export default Admin |
これで、Admin画面から編集画面に遷移することができるようになりました。
ページ遷移
EditMovieコンポーネントを修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// components/EditMovie.tsx ... const EditMovie = (props: any) => { ... const submit = async (e: any) => { .... await axios.post('admin/editmovie', JSON.stringify(movie)) .then((response) => { setAlert({ type: 'alert-success', message: 'Changes saved!' }) props.history.push({ // Admin画面へ飛ぶ pathname: "/admin" }) }) ... } ... } |
Saveボタンを押下したときに、Admin画面に遷移するようにしました。
検証
検証しましょう。ブラウザから「http://localhost:3000/admin」にアクセスしてください。
次回
次回は、バックエンド側でルーティング処理のセキュアな実装について触れていきます。
記事まとめ
参考書籍
ソースコード
movie-handlers.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
// cmd/api/movie-handlers.go package main import ( "backend/models" "encoding/json" "errors" "log" "net/http" "strconv" "time" "github.com/julienschmidt/httprouter" ) func (app *application) getOneMovie(w http.ResponseWriter, r *http.Request) { params := httprouter.ParamsFromContext(r.Context()) // urlからidを抜きだす id, err := strconv.Atoi(params.ByName("id")) if err != nil { app.logger.Print(errors.New("invalid id parameter")) app.errorJSON(w, err) return } movie, err := app.models.DB.Get(id) err = app.writeJSON(w, http.StatusOK, movie, "movie") if err != nil { app.errorJSON(w, err) return } } func (app *application) getAllMovie(w http.ResponseWriter, r *http.Request) { movies, err := app.models.DB.All() if err != nil { app.errorJSON(w, err) return } err = app.writeJSON(w, http.StatusOK, movies, "movies") if err != nil { app.errorJSON(w, err) return } } func (app *application) getAllGenres(w http.ResponseWriter, r *http.Request) { genres, err := app.models.DB.GenresAll() if err != nil { app.errorJSON(w, err) return } err = app.writeJSON(w, http.StatusOK, genres, "genres") if err != nil { app.errorJSON(w, err) return } } func (app *application) getAllMoviesByGenres(w http.ResponseWriter, r *http.Request) { params := httprouter.ParamsFromContext(r.Context()) genreID, err := strconv.Atoi(params.ByName("genre_id")) if err != nil { app.errorJSON(w, err) return } movies, err := app.models.DB.All(genreID) if err != nil { app.errorJSON(w, err) return } err = app.writeJSON(w, http.StatusOK, movies, "movies") if err != nil { app.errorJSON(w, err) return } } type MoviePayload struct { ID string `json:"id"` Title string `json:"title"` Description string `json:"Description"` Year string `json:"year"` ReleaseDate string `json:"release_date"` RunTime int `json:"runtime"` Rating int `json:"rating"` MPAARating string `json:"mpaa_rating"` } type jsonResp struct { OK bool `json:"ok"` Message string `json:"message"` } func (app *application) editMovie(w http.ResponseWriter, r *http.Request) { var payload MoviePayload err := json.NewDecoder(r.Body).Decode(&payload) if err != nil { log.Println(err) app.errorJSON(w, err) return } var movie models.Movie if payload.ID != "0" { id, _ := strconv.Atoi(payload.ID) // idからMovieデータを取得 m, _ := app.models.DB.Get(id) // ポインタを渡してm == movieにする movie = *m movie.UpdatedAt = time.Now() } movie.ID, _ = strconv.Atoi(payload.ID) movie.Title = payload.Title movie.Description = payload.Description movie.ReleaseDate, _ = time.Parse("2006-01-02", payload.ReleaseDate) movie.Year = movie.ReleaseDate.Year() movie.Runtime = payload.RunTime movie.Rating = payload.Rating movie.MPAARating = payload.MPAARating movie.CreatedAt = time.Now() movie.UpdatedAt = time.Now() if movie.ID == 0 { err = app.models.DB.InsertMovie(movie) if err != nil { app.errorJSON(w, err) return } } else { err = app.models.DB.UpdateMovie(movie) if err != nil { app.errorJSON(w, err) return } } ok := jsonResp{ OK: true, } err = app.writeJSON(w, http.StatusOK, ok, "response") if err != nil { app.errorJSON(w, err) return } } func (app *application) deleteMovie(w http.ResponseWriter, r *http.Request) { params := httprouter.ParamsFromContext(r.Context()) id, err := strconv.Atoi(params.ByName("id")) if err != nil { app.errorJSON(w, err) return } err = app.models.DB.DeleteMovie(id) if err != nil { app.errorJSON(w, err) return } ok := jsonResp{ OK: true, } err = app.writeJSON(w, http.StatusOK, ok, "reponse") if err != nil { app.errorJSON(w, err) return } } |
movies-db.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
// models/movies-db.go package models import ( "context" "database/sql" "fmt" "time" ) type DBModel struct { DB *sql.DB } // Get retuns one movie and error, if any func (m *DBModel) Get(id int) (*Movie, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() query := `select id, title, description, year, release_date, rating, runtime, mpaa_rating, created_at, updated_at from movies where id = $1` row := m.DB.QueryRowContext(ctx, query, id) var movie Movie err := row.Scan( &movie.ID, &movie.Title, &movie.Description, &movie.Year, &movie.ReleaseDate, &movie.Rating, &movie.Runtime, &movie.MPAARating, &movie.CreatedAt, &movie.UpdatedAt, ) if err != nil { return nil, err } // get genres, if any query = `select mg.id, mg.movie_id, mg.genre_id, g.genre_name from movies_genres mg left join genres g on (g.id = mg.genre_id) where mg.movie_id = $1` rows, _ := m.DB.QueryContext(ctx, query, id) defer rows.Close() genres := make(map[int]string) for rows.Next() { var mg MovieGenre err := rows.Scan( &mg.ID, &mg.MovieID, &mg.GenreID, &mg.Genre.GenreName, ) if err != nil { return nil, err } genres[mg.ID] = mg.Genre.GenreName } movie.MovieGenre = genres return &movie, nil } // All retuns all movies and error, if any func (m *DBModel) All(genre ...int) ([]*Movie, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() where := "" if len(genre) > 0 { where = fmt.Sprintf( "where id in (select movie_id from movies_genres where genre_id = %d)", genre[0]) } query := fmt.Sprintf( `select id, title, description, year, release_date, rating, runtime, mpaa_rating, created_at, updated_at from movies %s order by title`, where) rows, err := m.DB.QueryContext(ctx, query) if err != nil { return nil, err } defer rows.Close() var movies []*Movie for rows.Next() { var movie Movie err := rows.Scan( &movie.ID, &movie.Title, &movie.Description, &movie.Year, &movie.ReleaseDate, &movie.Rating, &movie.Runtime, &movie.MPAARating, &movie.CreatedAt, &movie.UpdatedAt, ) if err != nil { return nil, err } genreQuery := `select mg.id, mg.movie_id, mg.genre_id, g.genre_name from movies_genres mg left join genres g on (g.id = mg.genre_id) where mg.movie_id = $1` genreRows, _ := m.DB.QueryContext(ctx, genreQuery, movie.ID) genres := make(map[int]string) for genreRows.Next() { var mg MovieGenre err := genreRows.Scan( &mg.ID, &mg.MovieID, &mg.GenreID, &mg.Genre.GenreName, ) if err != nil { return nil, err } genres[mg.ID] = mg.Genre.GenreName } genreRows.Close() movie.MovieGenre = genres movies = append(movies, &movie) } return movies, nil } func (m *DBModel) GenresAll() ([]*Genre, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() query := `select id, genre_name, created_at, updated_at from genres order by genre_name` rows, err := m.DB.QueryContext(ctx, query) if err != nil { return nil, err } defer rows.Close() var genres []*Genre for rows.Next() { var g Genre err := rows.Scan( &g.ID, &g.GenreName, &g.CreatedAt, &g.UpdatedAt, ) if err != nil { return nil, err } genres = append(genres, &g) } return genres, nil } func (m *DBModel) InsertMovie(movie Movie) error { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() stmt := `insert movies (title, description, year, release_date, runtime, rating, mpaa_rating, created_at, updated_at) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` _, err := m.DB.ExecContext(ctx, stmt, movie.Title, movie.Description, movie.Year, movie.ReleaseDate, movie.Runtime, movie.Rating, movie.MPAARating, movie.CreatedAt, movie.UpdatedAt, ) if err != nil { return err } return nil } func (m *DBModel) UpdateMovie(movie Movie) error { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() stmt := `update movies set title = $1, description = $2, year = $3, release_date = $4, runtime = $5, rating = $6, mpaa_rating = $7, created_at = $8, updated_at = $9 where id = $10` _, err := m.DB.ExecContext(ctx, stmt, movie.Title, movie.Description, movie.Year, movie.ReleaseDate, movie.Runtime, movie.Rating, movie.MPAARating, movie.CreatedAt, movie.UpdatedAt, movie.ID, ) if err != nil { return err } return nil } func (m *DBModel) DeleteMovie(id int) error { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() stmt := "delete from movies where id = $1" _, err := m.DB.ExecContext(ctx, stmt, id) if err != nil { return err } return nil } |
routes.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// cmd/api/routes.go package main import ( "net/http" "github.com/julienschmidt/httprouter" ) func (app *application) routes() http.Handler { router := httprouter.New() router.HandlerFunc(http.MethodGet, "/status", app.statusHandler) router.HandlerFunc(http.MethodGet, "/v1/movie/:id", app.getOneMovie) router.HandlerFunc(http.MethodGet, "/v1/movies", app.getAllMovie) router.HandlerFunc(http.MethodGet, "/v1/movies/:genre_id", app.getAllMoviesByGenres) router.HandlerFunc(http.MethodGet, "/v1/genres", app.getAllGenres) router.HandlerFunc(http.MethodPost, "/v1/admin/editmovie", app.editMovie) router.HandlerFunc(http.MethodGet, "/v1/admin/deletemovie/:id", app.deleteMovie) return app.enableCORS(router) } |
Admin.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
// components/Admin.tsx import { Fragment, useEffect, useState } from "react" import { Movie } from '../models/movie' import { Link } from 'react-router-dom' import axios from 'axios' const Admin = () => { const [movies, setMovies] = useState<Movie[]>([]) const [isLoaded, setIsLoaded] = useState(false) const [error, setError] = useState("") useEffect(() => { ( async () => { await axios.get('movies') .then((response) => { setMovies(response.data.movies) setIsLoaded(true) }) .catch((err) => { setError(err.message) }) } )() }, []) if (error) { return ( <div>Error: {error}</div> ) } else if (!isLoaded) { return ( <p>Loading...</p> ) } return ( <Fragment> <h2>Manage Catalogue</h2> <hr /> <div className="list-group"> {movies.map((m) => { return ( <Link key={m.id} to={`/admin/movie/${m.id}`} className="list-group-item list-group-item-action" > {m.title} </Link> ) })} </div> </Fragment> ) } export default Admin |
EditMovie.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
// components/EditMovie.tsx import { Fragment, useEffect, useState, SyntheticEvent } from "react" import { AlertType } from '../models/ui-components' import { Movie } from '../models/movie' import { Link } from 'react-router-dom' import { confirmAlert } from 'react-confirm-alert' import 'react-confirm-alert/src/react-confirm-alert.css' import Input from './form-components/Input' import TextArea from './form-components/TextArea' import Select from './form-components/Select' import Alert from './ui-components/Alert' import './EditMovie.css' import axios from 'axios' const EditMovie = (props: any) => { const [movie, setMovie] = useState<Movie>() const [isLoaded, setIsLoaded] = useState(false) const [error, setError] = useState("") const [errors, setErrors] = useState<string[]>([]) const [alert, setAlert] = useState<AlertType>({type: "d-none", message: ""}) // form data const [id, setID] = useState(0) const [title, setTitle] = useState("") const [description, setDescription] = useState("") const [releaseDate, setReleaseDate] = useState("") const [runtime, setRuntime] = useState(0) const [rating, setRating] = useState(0) const [mpaaRating, setMpaaRating] = useState("") const [mpaaOptions, setMpaaOptions] = useState([{}]) useEffect(() => { ( async () => { setMpaaOptions([ {id: "G", value: "G"}, {id: "PG", value: "PG"}, {id: "PG13", value: "PG13"}, {id: "R", value: "R"}, {id: "NC17", value: "NC17"}, ]) // URLからIDを取得 setID(props.match.params.id) if (id > 0) { // Edit await axios.get(`movie/${id}`) .then((response) => { let movie = response.data.movie const releaseDate = new Date(movie.release_date) movie.release_date = releaseDate.toISOString().split("T")[0] setMovie(movie) setTitle(movie.title) setDescription(movie.description) setReleaseDate(movie.release_date) setRuntime(movie.runtime) setRating(movie.rating) setMpaaRating(movie.mpaa_rating) setIsLoaded(true) }) .catch((err) => { setError(err.message) }) } else { // New setIsLoaded(true) } } )() }, [id]) const submit = async (e: any) => { e.preventDefault() const movie: Movie = { id: id, title: title, description: description, year: "2021", release_date: releaseDate, runtime: Number(runtime), rating: Number(rating), mpaa_rating: mpaaRating, genres: [] } // validation let localErrors: string[] = [] if (movie?.title === "") { localErrors.push("title") } if (movie?.release_date === "") { localErrors.push("release_date") } if (movie?.runtime === 0) { localErrors.push("runtime") } if (movie?.rating === 0) { localErrors.push("rating") } setErrors(localErrors) if (errors.length > 0) { return false } await axios.post('admin/editmovie', JSON.stringify(movie)) .then((response) => { setAlert({ type: 'alert-success', message: 'Changes saved!' }) props.history.push({ pathname: "/admin" }) }) .catch((err) => { setError(err.message) setAlert({ type: 'alert-danger', message: err.response.data.error.message }) }) } const hasError = (key: string) => { return errors.indexOf(key) !== -1 } const confirmDelete = (e: SyntheticEvent) => { e.preventDefault() confirmAlert({ title: 'Delete Movie?', message: 'Are you sure?', buttons: [ { label: 'Yes', onClick: async () => { await axios.get(`admin/deletemovie/${id}`) .then((response) => { props.history.push({ pathname: "/admin" }) }) .catch((err) => { setError(err.message) setAlert({ type: 'alert-danger', message: err.response.data.error.message }) }) } }, { label: 'No', onClick: () => {} } ] }); } if (error) { // return ( // <div>Error: {error}</div> // ) } else if (!isLoaded) { return ( <p>Loading...</p> ) } return ( <Fragment> <h2>Add/Edit Movie</h2> <Alert alertType={alert.type} alertMessage={alert.message} /> <hr /> <form method="post" onSubmit={submit}> <Input title={"Title"} className={hasError("title") ? "is-invalid" : ""} type={'text'} name={'title'} value={title} handleChange={setTitle} errorDiv={hasError("title") ? "text-danger" : "d-none"} errorMsg={"Please enter a title"} /> <Input title={"Release Date"} className={hasError("release_date") ? "is-invalid" : ""} type={'date'} name={'release_date'} value={releaseDate} handleChange={setReleaseDate} errorDiv={hasError("release_date") ? "text-danger" : "d-none"} errorMsg={"Please enter a release_date"} /> <Input title={"Runtime"} className={hasError("runtime") ? "is-invalid" : ""} type={'text'} name={'runtime'} value={runtime} handleChange={setRuntime} errorDiv={hasError("runtime") ? "text-danger" : "d-none"} errorMsg={"Please enter a runtime"} /> <Select title={'MPAA Rating'} name={'mpaa_rating'} options={mpaaOptions} value={mpaaRating} handleChange={setMpaaRating} placeholder={'Choose...'} /> <Input title={"Rating"} className={hasError("rating") ? "is-invalid" : ""} type={'text'} name={'rating'} value={rating} handleChange={setRating} errorDiv={hasError("rating") ? "text-danger" : "d-none"} errorMsg={"Please enter a rating"} /> <TextArea title={"Description"} name={'description'} value={description} handleChange={setDescription} rows={3} /> <hr /> <button className="btn btn-primary" type="submit">Save</button> <Link to="/admin" className="btn btn-warning ms-1">Cancel</Link> {id > 0 && ( <a href="#!" onClick={confirmDelete} className="btn btn-danger ms-1"> Delete </a> )} </form> </Fragment> ) } export default EditMovie |
コメントを残す
コメントを投稿するにはログインしてください。