PROGRAMMING

チャット機能のメッセージを表示する【Vue.js】

今日は昨日から引き続き、メッセージを表示できるようにしていきます。

今日の内容
・チャットルームでメッセージの表示する
・メッセージを入力したものを表示する

これで今日はやっていきます。

今日の内容

メッセージの表示

https://iseblog.xyz/%e3%83%81%e3%83%a3%e3%83%83%e3%83%88%e3%83%ab%e3%83%bc%e3%83%a0%e3%81%ae%e4%bd%9c%e6%88%90%e3%80%90vue-js%e3%80%91/

昨日の内容でメッセージを保存することはできたのだが、他のファイルにmessagesを渡せていない。

つまり、_id.vueにはmessagesがあるが、messages.vueのファイルにはmessagesがないということだ。

コンポーネントはお互いが独立しているため、これを連結させる必要がある。

そしてこれをプロパティという機能で連結させようと思う。

コンポーネントについては下記のサイトが詳しく書かれてある。

<template>
  <div class="container">
    <div class="chats-layout">
       <messages :messages="messages" />
    </div>
    <div class="input-layout">
      <chat-form />
    </div>
  </div>
</template>

以上がプロパティの受け渡し方法である。

:messagesは任意のアトリビュートのため、:hogeでもなんでもいい。

"messages"dataの値を参照しているため、こちらはマスト。

次に受け取りの方法。Messagesファイルでは下記のように追加。

<script>
import Message from '~/components/Message'
export default {
  props: ['messages'],
  components: {
    Message,
  },
}
</script>

プロパティの略のpropsmessagesというプロパティを指定する。これは先ほど設定したmessages

そしてこのpropsの値を取得できるようにする。

<script>
import Message from '~/components/Message'
export default {
  components: {
    Message,
  },
  props: ['messages'],
  mounted() {
    console.log(this.messages)
  },
}
</script>

コンソールを確認してみる。

無事に確認することができた。

これで_id.vueからMessages.vueに値を渡すことができた。

ダミーのデータがチャットの画面に表示されていので、v-forを使ってmessagesのデータを表示できるようにする。

<template>
  <div class="chats-container">
    <message v-for="message in messages" :key="message" :message="message" />
  </div>
</template>

上のデータmessageMessage.vueで受け取る必要があるので、下記のように変更する必要がある。

<script>
export default {
  props: ['message'],
  computed: {
    displayname() {
      return '@' + this.message.user.name
    },
  },
}
</script>

これでMessages.vueからMessage.vuemessageを渡すことができた。

messageuserのデータ(name,thumbnail)がないからエラーが表示。

HTMLの方のuserデータを一旦コメントアウト。

<template>
  <div class="chat-container">
    <!--<div class="thumbnail-container">-->
    <!--  <img :src="message.user.thumbnail" />-->
    <!--</div>-->
    <div class="message-container">
      <!--<div class="user-name">{{ displayname }}</div>-->
      <div class="message">{{ message.text }}</div>
    </div>
  </div>
</template>

表示できたので、次はメッセージを入力して保存できるようにしようと思う。

メッセージを入力して表示

メッセージを入力できるようにしていく。

ChatForm.vueを変更していく。

<template>
  <div class="input-container">
    <textarea @click="login"></textarea>
  </div>
</template>

<script>
export default {
  methods: {
    login() {
      window.alert('ログインしてください')
    },
  },
}
</script>
<style scoped>
.input-container {
  padding: 10px;
  height: 100%;
}

textarea {
  width: 100%;
  height: 100%;
}
</style>

現在はこのようになっているので、ここから変更していく。

一旦ログイン機能を削除して、Firestore(データベース)にデータを追加できるようにする。

addメソッドを使って、データを追加するようにする。

それでは、以下のように変更していく。

<template>
  <div class="input-container">
    <textarea v-model="text"></textarea>
  </div>
</template>

<script>
export default {
  data(){
    return {
      text: 'テスト'
    }
  },
  methods: {
    login() {
      window.alert('ログインしてください')
    },
  },
}
</script>

v-modelを使い、HTMLで入力された値をJavaScriptで保存できるようにする。

v-modelを使うにはdataを定義する必要があるので、datatextを定義します。

textのなかにdataでに定義した'テスト'が表示された。

それでは、メソッドを定義して入力したメッセージを保存できるようにする。

<template>
  <div class="input-container">
    <textarea v-model="text"></textarea>
  </div>
</template>

<script>
import { db } from '~/plugins/firebase'
export default {
  data() {
    return {
      text: null,
    }
  },
  methods: {
    addMessage(){
      const channelId = this.$route.params.id
      db.collection('channels').doc(channelId).collection('messages').add({text: this.text})
    },
  },
}
</script>
<style scoped>
.input-container {
  padding: 10px;
  height: 100%;
}

textarea {
  width: 100%;
  height: 100%;
}
</style>

これでtextの中にdatatextを追加することができる。

textareaの中で、Enterキーを押してメッセージを保存する必要があるので、HTMLを下記のように変更。

<template>
  <div class="input-container">
    <textarea v-model="text" v-on:keydown.enter="addMessage"></textarea>
  </div>
</template>

v-on:keydown.enterでEnterキーが押されたらaddMessageメソッドを実行できるようになる。

一応アラートも追加する。

<script>
import { db } from '~/plugins/firebase'
export default {
  data() {
    return {
      text: null,
    }
  },
  methods: {
    addMessage() {
      const channelId = this.$route.params.id
      db.collection('channels')
        .doc(channelId)
        .collection('messages')
        .add({ text: this.text })
        .then(() => {
          alert('メッセージの保存に成功')
        })
    },
  },
}
</script>

firestoreに保存されたが、表示はされない。

それでは表示できるようにしていきたいが、問題発生。

  • 日本語の入力中に勝手にメッセージが保存されてしまう
  • テキストエリアの中にメッセージが残ってしまう

これを修正していく。

<script>
import { db } from '~/plugins/firebase'
export default {
  data() {
    return {
      text: null,
    }
  },
  methods: {
    addMessage(event) {
      if (event.keyCode === 229) { return }
      const channelId = this.$route.params.id
      db.collection('channels')
        .doc(channelId)
        .collection('messages')
        .add({ text: this.text })
        .then(() => {
          alert('メッセージの保存に成功')
        })
    },
  },
}
</script>

ここで出てくるif (event.keyCode === 229) {return}では、日本語入力中の場合はreturnしてメッセージを保存しないようにさせている。

229とは上のサイトでも記述されているように、日本語入力中の時のイベントを指す。

上記のコードでもいいのだが、可読性と見た目を考慮して、以下のように変更する。

<template>
  <div class="input-container">
    <textarea v-model="text" @keydown.enter="addMessage"></textarea>
  </div>
</template>

<script>
import { db } from '~/plugins/firebase'
export default {
  data() {
    return {
      text: null,
    }
  },
  methods: {
    addMessage(event) {
      if (this.enterJapaneseConversion(event)) { return }
      const channelId = this.$route.params.id
      db.collection('channels')
        .doc(channelId)
        .collection('messages')
        .add({ text: this.text })
        .then(() => {
          alert('メッセージの保存に成功')
        })
    },
    enterJapaneseConversion(event){
      const codeForConversion = 229
      return event.keyCode === codeForConversion
    }
  },
}
</script>
<style scoped>
.input-container {
  padding: 10px;
  height: 100%;
}

textarea {
  width: 100%;
  height: 100%;
}
</style>

こんな感じに変更。やっていることは読みやすくしただけ。

    enterJapaneseConversion(event) {
      const codeForConversion = 229
      return event.keyCode === codeForConversion
    },

enterJapaneseConversionというメソッドを作り(名前はテキトー)、event.keycodeの処理を追加しただけです。

これで日本語入力の際に、勝手に保存がされなくなりました。

しかし、テキストエリアにまだ残ったままでなので、 解決します。

addMessage(event) {
      if (this.enterJapaneseConversion(event)) {
        return
      }
      const channelId = this.$route.params.id
      db.collection('channels')
        .doc(channelId)
        .collection('messages')
        .add({ text: this.text })
        .then(() => {
          this.text = null
        })
    },

入力した後に、テキストの中身をなくせば消せるようになりますね。

これでメッセージの保存と表示は完了です。

まとめ

次回はリアルタイムでチャットをできるようにしていきます。

この状態だといちいちリロードしないといけないという問題があります。

次回はその問題を解決します。

最後まで読んでくださりありがとうございました。

-PROGRAMMING

Copyright © Iseblog ,@2020 All Rights Reserved.