質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

4257閲覧

Vue.js での methods 内の時間のカウントダウン

Bonito_Bonito

総合スコア67

Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2018/09/16 20:44

編集2018/09/16 20:49

Vue.js を使用し、開催イベントの終了までの残り時間を1秒ごとカウントダウンした表示を行いたく、下記のような記述をしているのですが、カウントダウンされずに読み込んだ時点での残り時間が表示されてしまいます。

各イベントの終了時刻は end_time に入っており、moment.js の diff により現在の時間からイベント終了時刻までを取って来て表示、というところまではできたのですが、(下記 noweventlist.vuecount() )その中の setInterval がうまく動作していない、という状況にあります。

now-event-list.json

json

1[ 2 { 3 "checkedin": false, 4 "begin_time": "2018-07-15T09:00:00.000Z", 5 "end_time": "2018-07-14T09:00:00.000Z", 6 }, 7 { 8 "checkedin": true, 9 "begin_time": "2018-07-14T09:00:00.000Z", 10 "end_time": "2018-07-14T09:00:00.000Z", 11 }, 12 { 13 "checkedin": true, 14 "begin_time": "2018-07-13T09:00:00.000Z", 15 "end_time": "2018-07-14T09:00:00.000Z", 16 }, 17 { 18 "checkedin": true, 19 "begin_time": "2018-07-12T09:00:00.000Z", 20 "end_time": "2018-07-13T09:00:00.000Z", 21 } 22]

index.pug

pug

1body 2 #app 3 .p-pane-target-container 4 .p-pane-target(:id='pane1[0].id' :class='{"is-visible":currentId1==pane1[0].id}') 5 .c-label 現在開催中のイベント 6 noweventlist(:noweventlistchild='noweventlist')

noweventlist.vue

vue

1<template lang="pug"> 2div 3 section.p-event-list.is-now-event(v-for='item in noweventlistchild') 4 .c-date-thumb 5 .c-date-thumb__year {{item.begin_time|year}} 6 .c-date-thumb__month {{item.begin_time|month}} 7 .c-date-thumb__date {{item.begin_time|date}} 8 .c-date-thumb__day {{item.begin_time|day}} 9 span.c-badge.p-event-list__badge.u-bg--secondary(v-if='item.unrd>0') {{item.unrd}} 10 .p-event-list__detail 11 .p-event-list__check-in-status 12 i.glyph.glyph-check-circle 13 span(v-if='item.checkedin') チェックインしています 14 span(v-else) チェックインしていません 15 .p-event-list__remain 残り時間 あと 16 span(v-html='count(item.end_time)') 17</template> 18 19<script> 20import moment from 'moment' 21export default { 22 props: ['noweventlistchild'], 23 methods: { 24 count(time) { 25 let diff = moment(time).diff(moment()) 26 // この箇所がうまく動作しません① 27 setInterval(()=>{ 28 diff = moment(time).diff(moment()) 29 },1000) 30 let duration = moment.duration(diff) 31 const days = Math.floor( duration.asDays()) 32 const hours = duration.hours() 33 const minutes = duration.minutes() 34 const seconds = duration.seconds() 35 let remain = `${days}日${hours}時間${minutes}分${seconds}秒` 36 // 上記①でなくこのように記述しても動作しません 37 setInterval(()=>{ 38 remain = `${days}日${hours}時間${minutes}分${seconds}秒` 39 },1000) 40 return remain 41 }, 42 }, 43 filters: { 44 format_test(time) { 45 return moment(time).format('YYYY/MM/DD HH:mm') 46 }, 47 year(time) { 48 return moment(time).format('YYYY') 49 }, 50 month(time) { 51 return moment(time).format('MM') 52 }, 53 date(time) { 54 return moment(time).format('DD') 55 }, 56 day(time) { 57 return moment(time).format('dddd') 58 }, 59 }, 60} 61</script>

app.js

js

1import Vue from 'vue' 2import axios from 'axios' 3import noweventlist from './components/noweventlist.vue' 4 5// ============================== 6// Vue インスタンス 7// ============================== 8const app = new Vue({ 9 el: '#app', 10 // 使うコンポーネント 11 components: { 12 noweventlist, 13 }, 14 data() { 15 return { 16 // 現在のイベントリスト格納用 17 noweventlist: [], 18 } 19 }, 20 21 created() { 22 // ------------------------------ 23 // 現在のイベントリスト表示用データ取得 24 // ------------------------------ 25 axios.get('../../__source/_data/now-event-list.json') 26 .then(function(response) { 27 this.noweventlist = response.data 28 }.bind(this)).catch(function(e){ 29 console.error(e) 30 }) 31 }, 32}) 33

Vue.js はまだ知見が足りないためご助力いただけますと幸いです。
また、記述面や意味がわからない等のご指摘もいただけますと幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

pugを全然知らないので間違ってたらすみません。

span(v-html='count(item.end_time)')これ、HTMLだと
<span v-html="count(item.end_time)"></span>って理解であってますよね?

v-html{{ }}で文字列を埋め込む代わりに、HTMLとして埋め込むだけです。
で、countメソッドの中身を見てみると、setIntervalの結果に関わらず、とりあえずreturn remain;してありますので、この時点の値がv-htmlによってHTMLとして解釈されて埋め込まれておしまいです。
あとからcountメソッド内のローカル変数を書き換えたところで、いったん埋め込んだ値を変更することにはなりません。

Vueで双方向バインディングをやるなら、メソッドではダメです。

ページが表示された時点ですでにカウントダウンは動作してていいって前提で書きます。

mountedフックを使うといいと思います。

このコンポーネントがマウントされた時点では、まだnoweventlistchildの中身が空っぽのようなので、watchを使ってプロパティを監視するように修正しました。

js

1export default { 2 props: ['noweventlistchild'], 3 data() { 4 return { 5 counts: [] // カウンタを持つ配列 6 }; 7 }, 8 methods: { 9 getDuration(endTime) { 10 let diff = moment(endTime).diff(moment()); 11 return moment.duration(diff); 12 }, 13 formatRemain(duration) { 14 const days = Math.floor( duration.asDays()); 15 const hours = duration.hours(); 16 const minutes = duration.minutes(); 17 const seconds = duration.seconds(); 18 19 return `${days}${hours}時間${minutes}${seconds}`; 20 } 21 }, 22 watch: { 23 noweventlistchild(new, old) { 24 // プロパティの配列でループ 25 new.forEach((item, index) => { 26 // dataに定義したcounts配列に初期値をぶっこむ 27 this.counts.push(this.formatRemain(this.getDuration(item.end_time))); 28 29 // 個別にsetIntervalをセットする 30 setInterval(() => { 31 // 自分のindexのカウンタを更新する 32 this.counts.splice(index, 1, this.formatRemain(this.getDuration(item.end_time))); 33 }, 1000); 34 }); 35 } 36 }

申し訳ないですが動作確認はできていないので、脳内イメージでコーディングしています。
こんな感じでカウントダウンが動作するはずです。
なんか間違ってたら適宜修正してください。

watch使えば、プロパティに渡ってきた配列の変更を検知してカウンタを追加したり削除したりすることもできると思います。

投稿2018/09/17 02:09

編集2018/09/17 21:57
spookybird

総合スコア1803

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Bonito_Bonito

2018/09/17 21:41

ありがとうございます。 pugについて、上記の認識で間違いありません! <template>〜</template> 内の span(v-html='count(item.end_time)') を span {{counts}} に置き換えて、ご提案のコードにしてみたのですが「残り時間 あと[]」とだけ表示されておりうまく動作しておらず… (おそらく this.noweventlistchild.forEach((item, index) => {〜〜〜}); のところではないかと思い、こちらで適宜調整しようと試みてはいるものの・・・)
Bonito_Bonito

2018/09/17 21:47

// 以下、 noweventlist.vue のファイル全体になります <template lang="pug"> div section.p-event-list.is-now-event(v-for='item in noweventlistchild') .c-date-thumb .c-date-thumb__year {{item.begin_time|year}} .c-date-thumb__month {{item.begin_time|month}} .c-date-thumb__date {{item.begin_time|date}} .c-date-thumb__day {{item.begin_time|day}} span.c-badge.p-event-list__badge.u-bg--secondary(v-if='item.unrd>0') {{item.unrd}} .p-event-list__detail .p-event-list__check-in-status i.glyph.glyph-check-circle span(v-if='item.checkedin') チェックインしています span(v-else) チェックインしていません .p-event-list__remain 残り時間 あと span {{counts}} //- <= ここがカウントダウン表示されず・・・ </template> <script> import moment from 'moment' export default { props: ['noweventlistchild'], data() { return { counts: [] // カウンタを持つ配列 }; }, methods: { getDuration(endTime) { let diff = moment(endTime).diff(moment()); return moment.duration(diff); }, formatRemain(duration) { const days = Math.floor( duration.asDays()); const hours = duration.hours(); const minutes = duration.minutes(); const seconds = duration.seconds(); return `${days}日${hours}時間${minutes}分${seconds}秒`; } }, mounted() { // プロパティの配列でループ // ただしmountedフックで一度動作するだけなので、 // 配列にあとから追加や削除があっても検知しない this.noweventlistchild.forEach((item, index) => { // dataに定義したcounts配列に初期値をぶっこむ this.counts.push(this.formatRemain(this.getDuration(item.end_time))); // 個別にsetIntervalをセットする setInterval(() => { // 自分のindexのカウンタを更新する this.counts.splice(index, 1, this.formatRemain(this.getDuration(item.end_time))); }, 1000); }); }, filters: { format_test(time) { return moment(time).format('YYYY/MM/DD HH:mm') }, year(time) { return moment(time).format('YYYY') }, month(time) { return moment(time).format('MM') }, date(time) { return moment(time).format('DD') }, day(time) { return moment(time).format('dddd') }, }, } </script>
spookybird

2018/09/17 21:52

あ、すみませんでした。app.jsの方までちゃんと見てませんでした。 非同期なサーバリクエストで取ってきているなら、mountedの時点では`noweventlistchild`の中身が空の可能性が高いですね。 内容修正します。
Bonito_Bonito

2018/09/17 22:21

ありがとうございます! 1秒ごとにカウントもされているのですが、「残り時間 あと[ "-66日-13時間-16分-35秒", "-67日-13時間-16分-35秒","〜","〜"・・・ ]」のようにオブジェクトの中身が全て出てしまいます。。 (span {{counts}} の箇所です)
spookybird

2018/09/17 22:25

v-for='(item, index) in noweventlistchild' こうやって、 {{counts[index]}} としてみてください。
Bonito_Bonito

2018/09/17 22:30

あ、なるほど…できました! 何から何まで、非常に助かりましたm(_ _)m
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問