Nuxt.js×Vuetify×Railsで検索機能の実装
上記の記事を参考に作成していこうと思います。
あくまで参考になれば程度ですのでコピペしても同じものは作成できないと思います。適宣コードは変えてください。
作成環境
"nuxt": "^2.15.7",
"vue-text-highlight": "^2.0.10",
"@nuxtjs/vuetify": "^1.12.1",
完成デモ動画
完成版ソースコード
<template>
<layout-main #layout-main><!--eslint-disable-line-->
<v-card>
<v-app-bar
flat
color="rgba(0, 0, 0, 0)"
>
<v-btn
icon
large
class="mr-5"
@click="pageBack"
>
<v-icon>mdi-arrow-left</v-icon>
</v-btn>
<v-toolbar-title>
検索
</v-toolbar-title>
</v-app-bar>
<v-divider />
<v-form>
<v-container>
<v-row
class="pt-7 pl-5 pr-5"
>
<v-text-field
v-model="keyword"
outlined
rounded
label="検索ワードを入力"
append-outer-icon="mdi-magnify"
@click:append-outer="search"
>
</v-text-field>
</v-row>
</v-container>
</v-form>
<v-divider />
<v-tabs
v-model="tab"
color="primary accent-10"
grow
>
<v-tab>投稿</v-tab>
<v-tab>ユーザー</v-tab>
</v-tabs>
<v-tabs-items v-model="tab">
<v-tab-item>
<div v-if="posts.length > 0">
<v-card-text class="ml-2">投稿検索({{ posts.length }})件</v-card-text>
</div>
<comment-card
v-for="post in posts"
:key="post.id"
:content-id="post.id"
:keyword="keyword"
/>
</v-tab-item>
<v-tab-item>
<div v-if="users.length > 0">
<v-card-text class="ml-2">ユーザー検索({{ users.length}})件</v-card-text>
</div>
<user-follow-card
v-for="user in users"
:key="user.id"
:user="user"
:followingUsers="followingUsers"
:keyword="keyword"
/>
</v-tab-item>
</v-tabs-items>
</v-card>
</layout-main>
</template>
<script>
import { mapGetters } from 'vuex'
import layoutMain from '../../components/layout/loggedIn/layoutMain.vue'
import commentCard from '../../components/post/commentCard.vue'
import userFollowCard from '../../components/user/userFollowCard.vue'
export default {
components: {
layoutMain,
commentCard,
userFollowCard
},
data () {
return {
tab: null,
keyword: '',
loading: false,
posts: [],
users: [],
followingUsers: []
}
},
computed: {
...mapGetters({
currentUserId: 'auth/currentUserId'
})
},
mounted () {
setTimeout(() => {
this.fetchFolowingUser()
}, 100)
},
methods: {
search () {
this.loading = true
const url = 'api/v1/search'
this.$axios.get(url, { params: { keyword: this.keyword } })
.then((res) => {
this.posts = res.data.posts
this.users = res.data.users
})
.catch((err) => {
console.log(err)
})
},
fetchFolowingUser () {
const url = `api/v1/users/${this.currentUserId}/following_users`
this.$axios.get(url)
.then((res) => {
this.followingUsers = res.data.following_users
})
.catch((err) => {
console.log(err)
})
},
pageBack () {
this.$router.go(-1)
}
}
}
</script>
フォームの作成
vuetifyのv-formを参考に作成していきます。
pagesでぃれくとりにsearches/index.vueを作成します。
<v-form>
<v-container>
<v-row
class="pt-7 pl-5 pr-5"
>
<v-text-field
v-model="keyword"
outlined
rounded
label="検索ワードを入力"
append-outer-icon="mdi-magnify"
@click:append-outer="search"
>
</v-text-field>
</v-row>
</v-container>
</v-form>
formのテキストフィールドとdataのkeywordをバインドさせます。
railsのコントローラー作成
docker compose run --rm api rails g controller searches
ルートの設定
get 'search' => "searches#search"
サーチメソッドを作成
今回はUserモデルとPostモデルを検索したかったためその2つのモデルにメソッドを追加します。
サーチコントローラの記述
dataオブジェクトの中にpostsとしてpostモデルの検索結果と、usersとしてuserモデルの検索結果を格納します。
Nuxt側の記述
dataにpostsとusersを用意し
formのボタンのクリックをされた時にv-on:click(@click)でsearchメソッドを発火させます。
ここまでで検索機能は完成しましたが、検索ワードにハイライトを付けたかったためハイライトを付けるプラグインを導入していきます。
検索ワードをハイライトするライブラリ「Vue Text Highlight」を導入します。
https://mebee.info/2020/05/09/post-8134/
何個か読むとどう導入していくかわかると思います。
つまずいた部分としてはVue Text Highlightはプレーンなテキストしかハイライトしてくれないということです。
どういうことかというと
vuetifyの<v-○○○>要素や、<component/>としたコンポーネント内のテキストには反映されないということです。
そのためコンポーネントをまたいでテキストにハイライトを付けい場合は
子コンポーネントのテキスト欄に<text-highlight/>コンポーネントをつけ、keywordというデータを親コンポーネントからpropsで受け渡します。
そうすることで子コンポーネントのテキストデータにもハイライトがつくことになります。
これで完成デモのような検索機能を作成することができました。