このサイトを読むとrails+VueでCRUDができるようになります。
多分駆け出しエンジニアさんが一番最初にバックエンドとフロントエンドで
やりたいことだと思うので、まずはやってみましょう。
環境構築はこちらから
Readはこちら
Createから行きます。
階層
├── App.vue
├── assets
│ └── vue.svg
├── components
│ └── HelloWorld.vue
├── lib
│ └── axios.ts
├── main.ts
├── router.ts
├── style.css
├── views
│ ├── BookEdit.vue
│ ├── Create.vue
│ └── Home.vue
└── vite-env.d.ts
yarn add vue-router
App.vue
<template>
<nav>
<router-link to="/">Home</router-link> | <router-link to="/create">Create</router-link> |
<router-link to="/BookEdit">edit</router-link>
</nav>
<main>
<router-view />
</main>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
package.json
{
"name": "vite-project",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.4.0",
"bootstrap": "^5.3.1",
"vue": "^3.3.4",
"vue-class-component": "^7.2.6",
"vue-property-decorator": "^9.1.2",
"vue-router": "^4.2.4"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"typescript": "^5.0.2",
"vite": "^4.4.5",
"vue-tsc": "^1.8.5"
}
main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
router.ts
import { createRouter, createWebHistory } from 'vue-router'
import Home from '@/views/Home.vue'
export default createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
component: Home,
},
{
path: '/create',
component: () => import('@/views/Create.vue'),
},
{
path: '/edit/:id',
component: () => import('@/views/BookEdit.vue'),
},
],
})
Home.vue
<template>
<div class="container">
<h1 class="#f3e5f5 purple lighten-5 center">[Rails+Vue.js]~Bookshelf~</h1>
<div class="row #e3f2fd blue lighten-5">
<div class="col s4 m6" v-for="book in books" :key="book.id">
<div class="card btn">
<span class="card-title" @click="setBookInfo(book.id)">
{{ book.title }}
</span>
</div>
</div>
</div>
<div class="row" v-show="bookInfoBool">
<div class="col s12 m12">
<div class="card blue-grey darken-1">
<div class="card-content white-text">
<span class="card-title">
【{{ bookInfo.title }}】
</span>
<div class="detail">
・著者:{{ bookInfo.author }}
</div>
<div class="detail">
・出版社:{{ bookInfo.publisher }}
</div>
<div class="detail">
・ジャンル:{{ bookInfo.genre }}
</div>
<router-link :to="{ path: `/edit/${bookInfo.id}` }" class="btn">本の編集</router-link>
<button class="btn #e53935 red darken-1" @click="deleteBook(bookInfo.id)">削除</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import axios from 'axios';
const bookInfo = ref({});
const bookInfoBool = ref(false);
const books = ref([]);
const deleteBook = (id: number) => {
axios.delete(`http://localhost:3000/api/books/${id}`).then(() => {
books.value = [];
bookInfo.value = '';
bookInfoBool.value = false;
fetchBooks();
});
};
const fetchBooks = () => {
axios.get('http://localhost:3000/api/books').then(
(res) => {
books.value = res.data;
},
(error) => {
console.log(error);
}
);
};
const setBookInfo = (id: number) => {
axios.get(`http://localhost:3000/api/books/${id}.json`).then((res) => {
bookInfo.value = res.data;
bookInfoBool.value = true;
});
};
onMounted(fetchBooks);
</script>
<style scoped></style>
Create.vue
<template>
<div class="container">
<h1 class="f3e5f5 purple lighten-5 center">本の登録</h1>
<form class="col">
<div class="row">
<div class="input-field">
<input placeholder="Title" type="text" class="validate" v-model="book.title" required />
</div>
</div>
<div class="row">
<div class="input-field">
<input placeholder="Author" type="text" class="validate" v-model="book.author" required />
</div>
</div>
<div class="row">
<div class="input-field">
<input placeholder="Publisher" type="text" class="validate" v-model="book.publisher" required />
</div>
</div>
<div class="row">
<div class="input-field">
<input placeholder="Genre" type="text" class="validate" v-model="book.genre" required />
</div>
</div>
<button class="btn btn-info waves-effect waves-light" @click="createBook">本を登録</button>
</form>
</div>
</template>
<script lang="ts">
import { ref } from 'vue';
import axios from 'axios';
import { Router, useRouter } from 'vue-router';
export default {
setup() {
const book = ref({
title: '',
author: '',
publisher: '',
genre: ''
});
const router: Router = useRouter();
const createBook = () => {
if (!book.value.title) return;
axios
.post('http://localhost:3000/api/books', { book: book.value })
.then((res) => {
router.push({ path: '/' });
})
.catch((error) => {
console.error(error);
});
};
return { book, createBook };
}
};
</script>
<style scoped></style>
books_controller.rb
class Api::BooksController < ApplicationController
def index
books = Book.all
render json: books
# render json: {book: books}
end
def show
book = Book.find(params[:id])
render json: book
end
def create
@book = Book.new(book_params)
if @book.save
head :no_content
else
render json: @book.errors, status: :unprocessable_entity
end
end
def update
@book = Book.find(params[:id])
if @book.update(book_params)
render json: @book
else
render json: @book.errors, status: :unprocessable_entity
end
end
def destroy
@book = Book.find(params[:id])
if @book.destroy
head :no_content
else
render json: @book.errors, status: :unprocessable_entity
end
end
def book_params
params.fetch(:book, {}).permit(
:title, :author, :publisher, :genre
)
end
end
完成
http://localhost:5173/create
edit
BookEdit.vue
<template>
<div class="container">
<h1 class="f3e5f5 purple lighten-5 center">本の編集</h1>
<form class="col s12">
<div class="row">
<div class="input-field">
<input placeholder="Title" type="text" class="validate" v-model="book.title" required />
</div>
</div>
<div class="row">
<div class="input-field">
<input placeholder="Author" type="text" class="validate" v-model="book.author" required />
</div>
</div>
<div class="row">
<div class="input-field">
<input placeholder="Publisher" type="text" class="validate" v-model="book.publisher" required />
</div>
</div>
<div class="row">
<div class="input-field">
<input placeholder="Genre" type="text" class="validate" v-model="book.genre" required />
</div>
</div>
<div class="btn" @click="updateBook(book.id)">本の情報を変更</div>
</form>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import axios from 'axios';
interface Book {
id: string;
title: string;
author: string;
publisher: string;
genre: string;
}
export default defineComponent({
name: 'BookEdit',
data() {
return {
id: this.$route.params.id,
book: {
id: '',
title: '',
author: '',
publisher: '',
genre: '',
} as Book,
};
},
mounted() {
this.setBookEdit(this.id);
},
methods: {
async setBookEdit(id: string) {
try {
const response = await axios.get<Book>(`http://localhost:3000/api/books/${id}.json`);
const data = response.data;
this.book.id = data.id;
this.book.title = data.title;
this.book.author = data.author;
this.book.publisher = data.publisher;
this.book.genre = data.genre;
} catch (error) {
console.error(error);
}
},
async updateBook(id: string) {
if (!this.book.title) return;
try {
await axios.put(`http://localhost:3000/api/books/${id}`, { book: this.book });
this.$router.push({ path: '/' });
} catch (error) {
console.error(error);
}
},
},
});
</script>
<style scoped></style>
下記を追加
Home.vue
<router-link :to="{ path: `/edit/${bookInfo.id}` }" class="btn">本の編集</router-link>
delete
Home.vue
<button class="btn #e53935 red darken-1" @click="deleteBook(bookInfo.id)">削除</button>
Home.vue
const deleteBook = (id: number) => {
axios.delete(`http://localhost:3000/api/books/${id}`).then(() => {
books.value = [];
bookInfo.value = '';
bookInfoBool.value = false;
fetchBooks();
});
};
追加
これでCRUDはできるはずです。
見にくいかもしれませんがHome.vueがわかればわかるはず。
いろんな人に助けてもらってここまでできました。
ありがとうございます。
ここどうなっているのとかこここう書いてほしいとか要望がありましたら言ってください。