1
0

More than 3 years have passed since last update.

#3 RailsとVue.jsでモーダルウィンドウを実装

Last updated at Posted at 2021-08-27

https://qiita.com/divclass123/items/61f592eb3663828eb931
前回の続き。
前回はかなり苦戦したが、なんとか実装できた。

しかし、子コンポーネントをモーダルウィンドウで表示して、そこでいいねを押してもリアルタイムで親コンポーネントにいいねが表示されないといった問題があった。
ちなみにリロードするとしっかりとどちらにも反映される。

(親)app.vueのなかに(app.vueから見たら子)show.vueもあって、(子)likeButton.vueもある。
(親,app.vueから見たら子)show.vueのなかに(子)likeButton.vueもある。
まぁ、なんかすこしややこしい。。。

app.vueのlikebutton.vueはしっかり機能してるし、
show.vueのなかのlikeButton.vueも機能してる。
問題はapp.vueからshow.vueをモーダルウィンドウで表示したときに、いいねがリアルタイムで反映されない。

[追記]色々あがいたが、きつかった。。。いつかリベンジ

app.vue
   <drinkShow :drink=drink></drinkShow>
  <likeButton :drinkId=drink.id></likeButton>
show.vue

<likeButton :drinkId=drink.id></likeButton>
likeButton.vue

<template>
  <div>
    <div v-if="isLiked" @click="deleteLike()">
      <div class="iine__button">
        <i class="fas fa-heart"></i> {{ count }}
      </div>

    </div>
    <div v-else @click="registerLike()">
      <div class="iine__button">
        <i class="far fa-heart"></i> {{ count }}
      </div>
    </div>
  </div>
</template>

<script>
import axios from 'axios'
// import { csrfToken } from 'rails-ujs'
// // CSRFトークンの取得とリクエストヘッダへの設定をしてくれます
// axios.defaults.headers.common['X-CSRF-TOKEN'] = csrfToken()



export default {
   props: ["drinkId"],
   // user.idはdrinks/index.html.erbに定義
   data(){
     return{
       likeList: []
       // いいね一覧を格納するための変数
     }
   },
   computed: {
     // データが変更されるたび動く
     // ここではlikeListが変更される度に、count,isLikedが再構築される
     count(){
       return this.likeList.length
       // いいね数を返す
     },
     isLiked(){
       // ログインユーザーが既にいいねしてるかを判定する
       if (this.likeList.length === 0){ return false}
            return Boolean(this.findLikeId())
     }
   },
   created: function(){
     // vueインスタンスの作成、初期化直後に実行される
     this.fetchLikeByDrinkId().then(result =>{
       console.log(result)
       this.likeList = result
     })
   },
   methods: {
    fetchLikeByDrinkId: async function(){
       // async function()
       // jsの非同期処理
        const response = await axios.get('/api/likes',{params: {drink_id:this.drinkId,user_id: user.id}})
        // await 
        // その投稿のいいね一覧を取得したい
        // もし処理が失敗したらプロセスから抜ける(処理をやめる?)
        return response.data


    },
     registerLike: async function(){
       // rails側のcreateアクションにリクエストするメソッド
       const response = await axios.post('/api/likes',{drink_id: this.drinkId,user_id: user.id})
       this.fetchLikeByDrinkId().then(result => {
        this.likeList = result

       })
     },
     deleteLike: async function(){
       // rails側のdestroyアクションにリクエストするメソッド
       const likeId = this.findLikeId()
       const response = await axios.delete(`/api/likes/${likeId}`,{params: {drink_id: this.drinkId,user_id: user.id}})
       this.likeList = this.likeList.filter(n => n.id !== likeId)
     },
     // ログインユーザーがいいねしているLikeモデルのidを返す
     findLikeId: function(){
       const like = this.likeList.find((like) => {
         return (like.user_id == user.id)
       })
       if (like) { return like.id }
     }
   }
}
</script>

https://qiita.com/TK-C/items/0a3acb9d0d310f8fd380
この記事みたけど、俺がやりたいこととは違っているような気がしていて、

発火させるイベントはどちらも、likeButton.vueのイベントだから。。。
親コンポーネントのイベントを発火させるわけではない。
親コンポーネントの中にある、子コンポーネントのイベントを発火するわけだから。

どうすればいいのか。。。

一旦。従来の子コンポーネントから親のイベントを発火させるといったやりかたでやってみよう。
app.vueはlikeButton.vueをインポートしてるから、likeButtonのイベントも発火してくれる
だろうと仮設を立てる。

いや、てかそもそもlikeButtonに書いてあることをapp.vueに書いてしまえばいいような気がする。
そしたら、show.vueから親コンポーネントのいいねに関するメソッドを呼び出せるし、、

app.vue


                  <div>
                    <div v-if="isLiked" @click="deleteLike()">
                      <div class="iine__button">
                        <i class="fas fa-heart"></i> {{ count }}
                      </div>

                    </div>
                    <div v-else @click="registerLike()">
                      <div class="iine__button">
                        <i class="far fa-heart"></i> {{ count }}
                      </div>
                    </div>
                  </div>

<script>

import axios from 'axios';
import likeButton from './packs/components/like/likeButton.vue';
import drinkShow from './packs/components/drinks/show.vue';


export default {
  components: {
      likeButton,
      drinkShow
  },
  data: function(){
    return {
      drinks: "drinks",
      likeList: []
    }
  },
  computed: {
     // データが変更されるたび動く
     // ここではlikeListが変更される度に、count,isLikedが再構築される
     count(){
       return this.likeList.length
       // いいね数を返す
     },
     isLiked(){
       // ログインユーザーが既にいいねしてるかを判定する
       if (this.likeList.length === 0){ return false}
            return Boolean(this.findLikeId())
     }
  },
  created: function(){
     // vueインスタンスの作成、初期化直後に実行される
     this.fetchLikeByDrinkId().then(result =>{
       console.log(result)
       this.likeList = result
     })
  },
  mounted(){
    this.setDrink();
  },
  methods: {
    setDrink: function(){
      axios.get('/api/drinks')
      .then(response =>(
        this.drinks = response.data
      ))
    },
    show : function(drink) {
        this.$modal.show(`display-drink-${drink.id}`);
    },
    hide : function () {
      this.$modal.hide('display-drink-show');
    },
    fetchLikeByDrinkId: async function(){
       // async function()
       // jsの非同期処理
        const response = await axios.get('/api/likes',{params: {drink_id:this.drinkId,user_id: user.id}})
        // await 
        // その投稿のいいね一覧を取得したい
        // もし処理が失敗したらプロセスから抜ける(処理をやめる?)
        return response.data


    },
    registerLike: async function(){
       // rails側のcreateアクションにリクエストするメソッド
       const response = await axios.post('/api/likes',{drink_id: this.drinkId,user_id: user.id})
       this.fetchLikeByDrinkId().then(result => {
        this.likeList = result

       })
    },
    deleteLike: async function(){
       // rails側のdestroyアクションにリクエストするメソッド
       const likeId = this.findLikeId()
       const response = await axios.delete(`/api/likes/${likeId}`,{params: {drink_id: this.drinkId,user_id: user.id}})
       this.likeList = this.likeList.filter(n => n.id !== likeId)
     },
     // ログインユーザーがいいねしているLikeモデルのidを返す
     findLikeId: function(){
       const like = this.likeList.find((like) => {
         return (like.user_id == user.id)
       })
       if (like) { return like.id }
    }
  }
}
</script>



likeButton.vueの内容をコピペ。

こうするときれいな親子関係ができた。

parent.vue
<likeButton :drinkId=drink.id></likeButton>
likeButton(child).vue

     registerLike: async function(){
       // rails側のcreateアクションにリクエストするメソッド
       const response = await axios.post('/api/likes',{drink_id: this.drinkId,user_id: user.id})
       this.fetchLikeByDrinkId().then(result => {
        this.likeList = result
       })
     },

というふうに、定義していましたが、

app.vue
    <li v-for="drink in drinks" :key="drink.id" class="list" >
                    <div v-else @click="registerLike(drink)">


<script>
    registerLike: async function(drink){
       // rails側のcreateアクションにリクエストするメソッド
       const response = await axios.post('/api/likes',{drink_id: drink.id,user_id: user.id})
       this.fetchLikeByDrinkId().then(result => {
        this.likeList = result

       })
    },

</script>

こんな感じで改めて定義し直す感じで。

って思ったら、

app.vue

    fetchLikeByDrinkId: async function(){
       // async function()
       // jsの非同期処理
        const response = await axios.get('/api/likes',{params: {drink_id: drink.id,user_id: user.id}})
        // await 
        // その投稿のいいね一覧を取得したい
        // もし処理が失敗したらプロセスから抜ける(処理をやめる?)
        return response.data


    },

のどの投稿にどれだけいいねがついてるかを見るメソッドで
drink単体のidを取得しなければならない。。。

なんか、いいねが2つ一気につくようになってる。

app.vue
 <div v-else @click="registerLike(drink)">
app.vue

    registerLike: async function(drink){
       // rails側のcreateアクションにリクエストするメソッド
       console.log(drink)
       const response = await axios.post('/api/likes',{drink_id: drink.id,user_id: user.id})
       this.fetchLikeByDrinkId(drink).then(result => {
        this.likeList = result

       })
    },
app.vue
    fetchLikeByDrinkId: async function(drink){
       // async function()
       // jsの非同期処理
       console.log(drink)
        const response = await axios.get('/api/likes',{params: {drink_id: drink.id,user_id: user.id}})
        // await 
        // その投稿のいいね一覧を取得したい
        // もし処理が失敗したらプロセスから抜ける(処理をやめる?)
        return response.data
    },

なんか、いいねが2つ一気につくようになってる。
ってなったのは

// created: function(){
// // vueインスタンスの作成、初期化直後に実行される
// this.fetchLikeByDrinkId().then(result =>{
// console.log(result)
// this.likeList = result
// })
// },
ここをコメントアウトしたから。

ほんとはいいねされてるけど、最初にいいねが表示されてないから、
2ついいねされてる感じになってる。。。。

created: function(){
// vueインスタンスの作成、初期化直後に実行される
this.drinks.forEach(drink=>
this.fetchLikeByDrinkId(drink).then(result =>{
console.log(result)
this.likeList = result
})
)
},

こんなかんじに記述。

  mounted(){
    this.setDrink();
  },
  methods: {
    setDrink: function(){
      axios.get('/api/drinks')
      .then(response =>(
        this.drinks = response.data
      ))
    },

でdrinksの配列を一つ一つとりだして、実行してる。
ただ、mountedよりcreatedのほうが早く実行されるから意味ない。


  created: function(){
     // vueインスタンスの作成、初期化直後に実行される
    this.setDrink();
  },
  mounted(){
      this.drinks.forEach(drink=>
        this.fetchLikeByDrinkId(drink).then(result =>{
       console.log(result)
       this.likeList = result
     })
    )
  },

こんな感じに書き換えた、したらfetchLikeByDrinkIdが最初の方にしっかりと動いてくれて
最初にいいねが表示されるんじゃないかと予想。

結論なんか動かない。。。

this.drinks.forEach isnot function とかになる。。。
なんで。。。

registerLike: async function(drink){で

console.log(this.drinksとかやるとアクセスできる。)

てかさいあく、show.vueにいいねボタンを表示せずに諦めればいい話ではある。

app.vue

<script>
  mounted(){
    this.setDrink();
  },
  methods: {
    setDrink: function(){
       axios.get('/api/memos')
       .then(response =>(
         response.data.forEach(drink=>
          this.fetchLikeByDrinkId(drink))
       ))
    },



    fetchLikeByDrinkId: async function(drink){
       // async function()
       // jsの非同期処理
        const response = await axios.get('/api/likes',{params: {drink_id: drink.id,user_id: user.id}})
        // await 
        // その投稿のいいね一覧を取得したい
        // もし処理が失敗したらプロセスから抜ける(処理をやめる?)
        console.log(response.data)
        return response.data
    },
</script>

受け取った、drinksの配列。つまり投稿の配列を一つ一つにたいして、forEachをして、 fetchLikeByDrinkId
をして、上手くいくと思ったがなんか上手くいかない。。。

   axios.get('/api/drinks')にするべきでした。。。

methods: {
setDrink: function(){
axios.get('/api/drinks')
.then(response =>(
response.data.forEach(drink=>
this.fetchLikeByDrinkId(drink))
))
},

改めてコンナ感じで定義

結局無理だったので、一旦諦めることに、、、、。
show.vueにはいいねボタンを表示しないことにしーとこ。。。。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0