投稿のユーザーの名前をクリックすると、そのユーザーの全投稿が表示される機能を
VueRouterを用いて作ったのだが、タイムラインに戻ったときにいいねがリアルタイムで反映されていない。。。
http://coffee-passport.net/
こちらで実際の挙動を確認していただければと思います。
(投稿一覧)
<template>
<div class='main' >
<div class="playing-button">
<button class="playing-button-on" @click="pauseVideo" v-if="playing">BGMをOFF</button>
<button class="playing-button-off" @click="playVideo" v-else>BGMをON</button>
</div>
<div class='item-contents'>
<router-view :user_id="user_id"></router-view>
<h2 class='title' id="title">タイムライン</h2>
<youtube :video-id="videoId" ref="youtube" @playerVars="playerVars" hidden/>
<ul class='item-lists' id="timeline">
<li v-for="drink in drinks" :key="drink.id" class="list" >
<router-link to="/user" @click.native="getUserId(drink.user_id)">
<div class="user-info-timeline" >
<div v-if="drink.user_img">
<img class="user-img-timeline" v-bind:src="drink.user_img" >
</div>
<div v-else>
<img class="user-img-timeline" src ="https://images.unsplash.com/photo-1469334031218-e382a71b716b?ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8YnJlYXV0aWZ1bCUyMGdpcmx8ZW58MHx8MHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=700&q=60">
</div>
<div class="username-timeline">
{{drink.nickname}}
</div>
</div>
</router-link>
<div class='item-img-content'>
<img class= "item-img" v-bind:src="drink.image" >
</div>
<div class='item-info'>
<h3 class="item-name">
{{drink.name }}
</h3>
<div class='item-price'>
<span>{{ drink.price }}円<br>(税込み)</span>
</div>
<div class='item-explain'>
{{drink.explain}}
</div>
<div class='item-tag'>
</div>
<div class="like-and-comment">
<likeButton :drinkId=drink.id></likeButton>
<i class="far fa-comment fa-lg"></i>
<button v-on:click="show(drink)">詳細を表示</button>
</div>
</div>
<modal v-bind:name="'display-drink-'+drink.id"
height="1000px"
styles="background-color: bisque">
<drinkShow :drink=drink></drinkShow>
</modal>
<div v-if="drink.body_id === 2" class="body-light"></div>
<div v-else-if="drink.body_id === 3" class="body-medium"></div>
<div v-else-if="drink.body_id === 4" class="body-full"></div>
<div v-else class="body-nothing"></div>
<div v-if="drink.acidity_id === 2" class="acidity-low"></div>
<div v-else-if="drink.acidity_id === 3" class="acidity-medium"></div>
<div v-else-if="drink.acidity_id === 4" class="acidity-high"></div>
<div v-else class="acidity-nothing"></div>
</li>
</ul>
</div>
</div>
</template>
<script>
import axios from 'axios';
import likeButton from './packs/components/like/likeButton.vue';
import drinkShow from './packs/components/drinks/show.vue';
export default {
name: 'player',
props: {
src: "https://www.youtube.com/watch?v=02azSAMtZWU"
},
components: {
likeButton,
drinkShow,
},
data: function(){
return {
drinks: "drinks",
user_id: 0,
videoId: "QN1uygzp56s",
playing: false,
playerVars: {
autoplay: 1
}
}
},
created(){
},
mounted: function(){
// this.playVideo();
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');
},
playVideo(){ // 再生処理
this.$refs.youtube.player.playVideo()
this.playing = true
},
pauseVideo(){ // 停止処理
this.$refs.youtube.player.pauseVideo()
this.playing = false
},
getUserId(user_id){
this.user_id = user_id
document.getElementById("timeline").style.visibility ="hidden";
document.getElementById("title").style.visibility ="hidden";
scrollTo(0, 0);
}
}
}
</script>
(ユーザーの投稿)
<template>
<div class='main'>
<div class='item-contents'>
<h2 class='title'></h2>
<router-link to="/" @click.native="showTimeline()" class="back-to-timeline">タイムラインに戻る</router-link>
<ul class='item-lists'>
<li v-for="drink in filtredDrinks" :key="drink.id" class="list" >
<div class="user-info-timeline" >
<div v-if="drink.user_img">
<img class="user-img-timeline" v-bind:src="drink.user_img" >
</div>
<div v-else>
<img class="user-img-timeline" src ="https://images.unsplash.com/photo-1469334031218-e382a71b716b?ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8YnJlYXV0aWZ1bCUyMGdpcmx8ZW58MHx8MHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=700&q=60">
</div>
<div class="username-timeline">
{{drink.nickname}}
</div>
</div>
<div class='item-img-content'>
<img class= "item-img" v-bind:src="drink.image" >
</div>
<div class='item-info'>
<h3 class="item-name">
{{drink.name }}
</h3>
<div class='item-price'>
<span>{{ drink.price }}円<br>(税込み)</span>
</div>
<div class='item-explain'>
{{drink.explain}}
</div>
<div class='item-tag'>
</div>
<div class="like-and-comment">
<likeButton :drinkId=drink.id></likeButton>
<i class="far fa-comment fa-lg"></i>
<button v-on:click="show(drink)">詳細を表示</button>
</div>
</div>
<modal v-bind:name="'display-drink-'+drink.id"
height="1000px"
styles="background-color: bisque">
<drinkShow :drink=drink></drinkShow>
</modal>
<div v-if="drink.body_id === 2" class="body-light"></div>
<div v-else-if="drink.body_id === 3" class="body-medium"></div>
<div v-else-if="drink.body_id === 4" class="body-full"></div>
<div v-else class="body-nothing"></div>
<div v-if="drink.acidity_id === 2" class="acidity-low"></div>
<div v-else-if="drink.acidity_id === 3" class="acidity-medium"></div>
<div v-else-if="drink.acidity_id === 4" class="acidity-high"></div>
<div v-else class="acidity-nothing"></div>
</li>
</ul>
</div>
</div>
</template>
<script>
import Vue from 'vue/dist/vue.esm.js'
import VModal from 'vue-js-modal'
import axios from 'axios';
import likeButton from '../like/likeButton.vue';
import drinkShow from '../drinks/show.vue';
Vue.use(VModal)
export default {
props: {
user_id: {
type: Number
}
},
components: {
likeButton,
drinkShow
},
data: function(){
return {
filtredDrinks: [],
drinks: "drinks",
userName: ""
}
},
created(){
this.setDrink();
},
methods: {
setDrink: function(){
axios.get('/api/drinks')
.then(response => {
const drinks = response.data
this.filtredDrinks = drinks.filter(drink =>
drink.user_id === this.user_id)
})
},
show: function(drink) {
this.$modal.show(`display-drink-${drink.id}`);
},
hide: function () {
this.$modal.hide(`display-drink-${drink.id}`);
},
showTimeline: function(){
document.getElementById("timeline").style.visibility ="visible";
document.getElementById("title").style.visibility ="visible";
}
},
}
</script>
<style scoped>
.back-to-timeline{
color: white;
background-color:#3c1900 ;
padding: 5px;
border-bottom: solid 4px black;
border-radius: 3px;
transition: background-color 5s ease-out;
}
.back-to-timeline:active{
-webkit-transform: translateY(4px);
transform: translateY(4px);/*下に動く*/
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.2);/*影を小さく*/
border-bottom: none;
}
.back-to-timeline:hover{
background: linear-gradient(270deg, black 0%, maroon 50%, #3c1900 100%);
}
.back-to-timeline a:visited{
color: white;
background-color:#3c1900 ;
padding: 5px;
}
</style>
<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>
app.vueのコンポーネントのいいねボタンと、user.vueの子コンポーネントのいいねを同期させたい。
VueRouterでuser.vueを表示させてるから分からんけど、
app.vueが親、app.vueの中にあるrouter-linkで表示させたuser.vueが子、user.vue内でインポートしてるlikeButton.vueが孫って一旦仮定していいのかな。。。
そうね、app.vueが親、app.vueの中にあるrouter-linkで表示させたuser.vueが子っていう概念でいいっぽい。
もう一回整理すると、
app.vueの子コンポーネントであるlikeButton.vueのイベントと、
app.vueの子コンポーネントであるuser.vueの子コンポーネントであるlikeButton.vueの
イベントを同期させたい。。。
なんて検索したらいいんだろう。。。
$emitはそのまま親コンポーネントのイベントを発火させるが、
親コンポーネントのなかの子コンポーネントのイベントを発火させたい。。。