前置き
チャットシリーズの続きです✨
今回はVuex
に移行していきます❣️
これが終わったら
本当にCSSです…😂笑
【Nuxt.js】開発ブログ:firebaseでチャットアプリ①
【Nuxt.js】開発ブログ:firebaseでチャットアプリ②
【Nuxt.js】開発ブログ:firebaseでチャットアプリ③
Vuexに移行
⬇️Vuexは解説記事を
いくつか出していますが
簡潔に使い方を
まとめたものはこちらです❤️
【Nuxt.js】Vuexまとめ編:はじめる前に、簡単理解!
まずは取得データのpostsを移行
index.vueにあったposts
を
store/index.jsに移します🚗💨
fetch
でactions
を呼び出して
最初にposts
を取得していきます。state
の上書きはmutations
でしかできないのでactions
の最後にcommit
で呼び出します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import axios from 'axios' import firebase from '@/plugins/firebase' export const state = () => ({ posts: [], }) export const getters = { posts: state => { return state.posts }, } export const actions = { getData ({ commit }) { return axios .get('https://sample-6a560.firebaseio.com/message.json') .then((res) => { const postArray = [] for (const key in res.data) { let allKey = res.data[key] postArray.push(allKey) } commit('getData', postArray) }) } } export const mutations = { getData (state, postArray) { state.posts = postArray }, } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
<template> <div class="page"> <form class="form" @submit.prevent > <label class="label"> <span class="label"> email </span> <input class="input" type="text" v-model="email" > </label> <label class="label"> <span class="label"> password </span> <input class="input" type="password" v-model="password" > </label> <button class="button" type="submit" @click="login" > Login </button> </form> <ul> <li v-for="post in posts" :key="post.id" :class="{ me: user.email === post.userId}" > <div> {{ post.name }} | {{ $dayjs(post.time).format('MM-DD HH:mm') }} <img :src="post.thumbnail" alt=""> <p v-html="post.message">{{ post.message }}</p> </div> </li> </ul> <form @submit.prevent> <label class="img" > <input type="file" class="file" @change="changeImg" > </label> <img :src="user.thumbnail" alt=""> <label> <span> 名前: </span> <input type="text" v-model="user.name" > </label> <label> <span> message: </span> <textarea v-model="user.message" > </textarea> </label> <button type="submit" @click="submit" > Submit </button> </form> </div> </template> <script> import axios from 'axios' import firebase from '@/plugins/firebase' export default { async fetch ({ store }) { await store.dispatch('chat/getData') }, computed: { posts () { return this.$store.getters['chat/posts'] }, }, data () { return { email: '', password: '', thumbnail: '', user: { email: '', name: "", message: "", thumbnail: '', }, } }, methods: { submit () { let Ref = firebase.database().ref().child('message') let stamp = firebase.database.ServerValue.TIMESTAMP Ref.push({ name: this.user.name, message: this.user.message, userId: this.user.email, time: stamp, thumbnail: this.user.thumbnail }) .then(response => { this.getData () this.user.name = '' this.user.message = '' }) let storage = firebase.storage() let storageRef = storage.ref().child('file.png') storageRef.put(this.thumbnail) .then(res => console.log(res)) .catch(error => console.log(error)) }, login() { firebase.auth().signInWithEmailAndPassword(this.email, this.password) .then(data => { console.log(data.user.email) this.user.email = data.user.email }).catch((error) => { console.log(error) }); }, changeImg (e) { this.thumbnail = e.target.files[0] if (this.thumbnail) { const reader = new FileReader() reader.onload = () => { this.user.thumbnail = reader.result + '' } reader.readAsDataURL(this.thumbnail) } }, }, } </script> |
あとはsubmit
を最後にして
他の物から移行していきます。
loginを移行
mutations
でstate
の上書きをしていても
このエラーが出ました。
一旦配列にpush
すれば解決。
Error
Do not mutate vuex store state outside mutation handlers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
import axios from 'axios' import firebase from '@/plugins/firebase' export const state = () => ({ posts: [], loginUser: { email: '', password: '', }, }) export const getters = { posts: state => { return state.posts }, user: state => { return state.user }, } export const actions = { login ({commit}, login) { firebase.auth().signInWithEmailAndPassword(login.email, login.password) .then(data => { console.log(data) commit('login', data) }).catch((error) => { console.log(error) }) }, getData ({ commit }) { return axios .get('https://sample-6a560.firebaseio.com/message.json') .then((res) => { const postArray = [] for (const key in res.data) { let allKey = res.data[key] postArray.push(allKey) } commit('getData', postArray) }) }, submit ({ commit }, { name, message, email, thumnail }) { let Ref = firebase.database().ref().child('message') let stamp = firebase.database.ServerValue.TIMESTAMP Ref.push({ name: name, message: message, userId: email, time: stamp, thumbnail: thumbnail }) .then(response => { dispatch('getData') }) let storage = firebase.storage() let storageRef = storage.ref().child('file.png') storageRef.put(thumbnail) .then(res => console.log(res)) .catch(error => console.log(error)) }, } export const mutations = { getData (state, postArray) { state.posts = postArray }, login (state, data) { let loginData = [] loginData.push.data state.loginUser = loginData }, } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
<template> <div class="page"> <form class="form" @submit.prevent > <label class="label"> <span class="label"> email </span> <input class="input" type="text" v-model="email" > </label> <label class="label"> <span class="label"> password </span> <input class="input" type="password" v-model="password" > </label> <button class="button" type="submit" @click="login" > Login </button> </form> <ul> <li v-for="post in posts" :key="post.id" :class="{ me: user.email === post.userId}" > <div> {{ post.name }} | {{ $dayjs(post.time).format('MM-DD HH:mm') }} <img :src="post.thumbnail" alt=""> <p v-html="post.message">{{ post.message }}</p> </div> </li> </ul> <form @submit.prevent> <label class="img" > <input type="file" class="file" @change="changeImg" > </label> <img :src="user.thumbnail" alt=""> <label> <span> 名前: </span> <input type="text" v-model="user.name" > </label> <label> <span> message: </span> <textarea v-model="user.message" > </textarea> </label> <button type="submit" @click="submit" > Submit </button> </form> </div> </template> <script> import axios from 'axios' import firebase from '@/plugins/firebase' export default { async fetch ({ store }) { await store.dispatch('chat/getData') }, computed: { posts () { return this.$store.getters['chat/posts'] }, loginUser () { return this.$store.getters['chat/loginUser'] }, }, data () { return { email: '', password: '', thumbnail: '', user: { email: '', name: "", message: "", thumbnail: '', }, } }, methods: { submit () { this.$store.dispatch('chat/submit', { name: this.user.name, message: this.user.message, userId: this.user.email, thumbnail: this.user.thumbnail }) this.user.name = '' this.user.message = '' }, login(email, password) { this.$store.dispatch('chat/login', { email: this.email, password: this.password }) }, changeImg (e) { this.thumbnail = e.target.files[0] if (this.thumbnail) { const reader = new FileReader() reader.onload = () => { this.user.thumbnail = reader.result + '' } reader.readAsDataURL(this.thumbnail) } }, }, } </script> |
画像部分の移行
input
で選択した画像を表示させつつ、submit
した時にデータを送信📤
ということでreader.readAsDataURL(thumbnail)
して
urlをstate
に入れて
表示させようとしたのですが
これはできませんでした。<img :src="$store.getters['change/thumbnail']" alt="">
バインドするdata
がstate
に置き換わっているので
こう書くしかないんですよね…。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<template> <div class="page"> <form @submit.prevent> <label class="img" > <input type="file" class="file" @change="changeImg" > </label> <img :src="this.$store.getters['change/thumbnail']" alt=""> <button type="submit" @click="submit" > Submit </button> </form> </div> </template> <script> import axios from 'axios' import firebase from '@/plugins/firebase' export default { data () { return { user: { thumbnail: '', }, } }, methods: { submit () { this.$store.dispatch('change/submit') }, changeImg (e) { this.$store.dispatch('change/change', { e: e, thumbnail: this.thumbnail, userThumbnail: this.user.thumbnail }) }, }, } </script> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
import axios from 'axios' import firebase from '@/plugins/firebase' export const state = () => ({ thumbnail: '', }) export const getters = { thumbnail: state => { return state.thumbnail }, } export const actions = { submit ({ commit }, { name, message, email, thumnail }) { // 省略 }, change ({ commit }, { e, thumbnail, userThumbnail }) { thumbnail = e.target.files[0] if (thumbnail) { const reader = new FileReader() reader.onload = () => { console.log(e.target.result) userThumbnail = reader.result + '' } reader.readAsDataURL(thumbnail) } commit('change', thumbnail, userThumbnail) }, } export const mutations = { change (state, thumbnail, userThumbnail) { state.thumbnail = userThumbnail } } |
そもそもimg
で表示させるのではなくinput
に表示させてみる方が
良いかもしれません🤔💭
というより、
プレビューだけならVuex
にする必要もないかもです。reader
で何をしているか明確にし、
分けていこうと思います💡
textareaの選択した文字に装飾(ボツネタ)
コードを共有したい時
選択範囲のテキストを
コード用の装飾に変更します。
なのでtextarea
内の
選択範囲の先頭と後ろに
タグを追加して
実装しようとしていました。
ただ、
そもそもtextarea
に
タグを入れてはいけない
ようで…
ボツになりました笑
参考になるか分かりませんが
それまでの道のりを
記しておきます…。
参考:
javascript 選択中のテキストを取得する
範囲選択した文字列を修飾する
JavaScript初級者から中級者になろう
v-htmlで表示確認
とりあえず
メッセージ入力部分がinput
になっていたのでtextarea
に変更。
textarea
に
手打ちでタグを適当に入れてv-html
で表示させてみます。<p style="color: red;">red</p>
firebase上では
こんな感じ。
表示部分
1 2 3 4 5 |
<div> {{ post.name }} | | {{ $dayjs(post.time).format('MM-DD HH:mm') }} <img :src="post.thumbnail" alt=""> <p v-html="post.message">{{ post.message }}</p> </div> |
ということで
あとは選択範囲を認識させてspan
タグとかをつけて
firebaseに送信できれば
大丈夫そうです🙆♀️
選択範囲の認識
そもそもtextareaに
タグを直接入れてはいけないので<
は>
>
は<
に変更する必要はありそう。
参考:
選択(Selection) と 範囲(Range)
テキストエリア内にタグ?
select
イベントで
まずは選択範囲の
文字列だけ取得します💫
1 2 3 4 5 6 7 8 9 10 |
<label> <span> message: </span> <textarea v-model="user.message" @select="select" > </textarea> </label> |
methods
で
選択範囲のSelection
オブジェクトを取得。
1 2 3 |
select () { let obj = document.getSelection() }, |
ここからRangeオブジェクトを取得
とりあえずgetRangeAtの引数は
空ではダメっぽい。
TypeError: Failed to execute 'getRangeAt' on 'Selection': 1 argument required, but only 0 present.
range
にnode
追加
とりあえずnode
の種類もよく分からないのでcreateTextNode
を使ってみました。
https://qiita.com/mima_ita/items/bacf3ca3c078d1e6c70f
どこにタグが来るのか
チェックするためこれだけ追記。
1 2 3 4 5 6 7 |
select () { let obj = document.getSelection() let range = obj.getRangeAt(0) console.log(range) let textNode = document.createTextNode("<span>") range.insertNode(textNode) }, |
ところが。。。
textarea内の文字列を選択しても
枠の外にタグが追加されます。。。
そもそもタグではなく文字列扱いに。createElement
も試しましたが
何も追加されませんでした。
やっぱりtextarea
に
タグを入れること自体
よくないようですね。
無難にエディタを使用した方が良さそうです。
https://qiita.com/mima_ita/items/bacf3ca3c078d1e6c70f
まとめ
Vuex
に慣れてきたものの、
画像のところで
詰まってしまいました😖💦
ただ次にやれば良いことが
見えてきたので必ず移行させます…❗️
移行して機能に問題なければ
CSS整えて完成予定です…❣️
そして今回はボツになってしまった
選択範囲の装飾。
CSSとかの方がよっぽど大事ですが
気になるので調べまくりました。。。
でも無駄だったというのが
とても悲しいです😭笑
まぁそんな時もありますよね←