- 投稿日:2020-05-29T22:29:38+09:00
BootstrapVueのダサいイメージコンポーネントをクールなデザインにカスタマイズしてみた
Vueバージョン確認
npm list vueまずは上記コマンドでバージョンの確認
twinzlabo@0.1.0 /Users/twinzlabo ── vue@2.6.11BootstrapVueの導入
BootstrapVueの導入がまだの方のために念のため導入方法書いときますね
とりあえずコピペして環境を整えてください
main.jsimport BootstrapVue from 'bootstrap-vue' import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap-vue/dist/bootstrap-vue.css' Vue.use(BootstrapVue)npm install vue bootstrap-vue bootstrap以上でBootstrapVueの導入は完了です
BootstrapVueのダサいイメージをクールなデザインにカスタマイズ
すでに上の方で確認してもらったかと思いますが、
BootstrapVueの非常にダサいImageコンポーネントをスタイル修正を行うことでクールなデザインに編集していきましょう
デフォルトの上の画像をhoverしたら下の画像のように色がつくようにカスタマイズしていきます
この感じなかなかクールですよね
では早速コードをコピペしていきましょう
<template> <div> <b-img src="https://picsum.photos/300/150/?image=41" fluid alt="Fluid image"></b-img> </div> </template><style> img { margin-right: 10px; margin-bottom: 30px; display: inline-block; width: 500px; height: 400px; background-size: contain; background-repeat: no-repeat; cursor: pointer; transition: all 200ms ease-in; filter: grayscale(1) opacity(.8); } img:hover { filter: grayscale(0) opacity(1); } </style>これだけです
いかがでしたでしょうか?ちゃんと同じようなデザインになりましたか?
今回も超簡単にカスタマイズできましたね
以上です
参考記事(より応用的な実装が希望の方)
【Vueデザイン/コピペだけ】白黒画像一覧でhoverすると色がつくCSSアニメーション実装を徹底解説
←トップ画像のような実装ができます
【Vue/BootstrapVueコピペのみ】Bootstarap導入からシンプルな画像一覧画面の実装方法までを徹底解説
- 投稿日:2020-05-29T19:53:07+09:00
Vue.js 2 + Vuex State Management Tutorial by Example
Vue is an excellent open-source front-end library to build user-friendly web/mobile applications. It follows the same component-based architecture as other popular frameworks do.
click here to read more:
https://www.positronx.io/vue-js-vuex-state-management-tutorial-by-example/
- 投稿日:2020-05-29T19:52:21+09:00
Vue i18n Tutorial – How to Build Multi-Lingual Vue.js App
Learn how to translate the Vue.js app in multi-language using the vue-i18n plugin.
click here to read more:
https://www.positronx.io/vue-i18n-tutorial-how-to-build-multi-lingual-vue-js-app/
- 投稿日:2020-05-29T19:50:33+09:00
Vue.js 2 + Firebase Authentication Tutorial Example
Learn how to integrate Firebase Authentication in Vue.js 2+ application. We will also learn to register a user, login a user and send password reset mail and logout from firebase app using Vue.js application.
click here to read more:
https://www.positronx.io/vue-js-firebase-authentication-tutorial-example/
- 投稿日:2020-05-29T19:48:54+09:00
Deploy Vue.js 2+ App to Firebase Hosting in Less Than 5 Minutes
In this tutorial, we are going to explain step by step how to deploy Vue.js application on Free Firebase Hosting using Firebase console.
click here to read more:
https://www.positronx.io/deploy-vue-js-app-to-firebase-hosting/
- 投稿日:2020-05-29T19:21:36+09:00
【Vue.js】 DOMを直接操作 $el $ref
はじめに
データバインディングを利用することでDOMの更新は効率化されるが、直接DOMを参照したい場合もある。例えば、画面上の要素の位置や高さはDOMを直接参照しなければ分からない。
DOMに直接アクセスするにはインスタンスプロパティの$el
と$refs
を使用する。
この2つはDOMを参照するため、ライフサイクルのmounted以降でなければ使用できない
。$el(ルートを参照)
$elを使用することでインスタンスやコンポーネントのルートを参照することができる。
sample.html<div id="app"> <p>Hello</p> </div>sample.jsnew Vew({ el: 'app', mounted: function() { // <div id="app"></div>が出力される console.log(this.$el) } })ルートとは
HTMLはツリー構造になっているが、その最上位に位置するのがルートである。HTMLドキュメントでは
html
要素がルートにあたる。$refs(DOMの要素を参照)
ルート以外のDOMの要素を参照する場合は、
ref
属性と$refs
を使用する。
ref
はDOM要素を参照するための特別な属性で$refs
のキーとなる。sample.html<div id="app"> <!-- 対象となる要素にref属性で名前をつける --> <p ref="hello">Hello</p> </div>sample.jsnew Vue({ el: 'app', mounted: function() { // <p>Hello</p>が出力される console.log(this.$refs.hello); } })動的にDOMを参照
動的にする場合、refはv-bindディレクティブを使って
:ref
と記述する。
v-for
で要素を繰り返し表示している場合はv-for部分は配列構造になっているのでscript内でアクセスするにはthis.$refs[ref属性でつけた名前]
と記述する。sample.html<div v-for="item in items" :key="item.id"> <!-- 動的にするためrefにv-bindをつける --> <p :ref="name">{{ item.name }}</p> </div>sample.jsnew Vue ({ el: 'app', data: { items: [ { id: '001', name: 'apple' }, { id: '002', name: 'orange' } ] }, mounted: function() { // <p>apple</p>が出力される console.log(this.$refs[name][0]); } })$elと$refsは一時的な変更
$elと$refsは一時的な変更なので、データが更新されたり、アプリケーションが操作されて再描画されると上書きされる(変更がリセットされる)。仮想DOMではなくリアルDOMを参照しているからである。
リアルDOMと仮想DOM
Vueには
リアルDOM
と仮想DOM
という2つのDOMが存在する。リアルDOM
リアルDOMは実際にブラウザを描画しているものである。
Vueを使用しないでリアルDOMのみで描画している場合、変更を加えた際はHTML内の全てを読み込むので描画に時間がかかる。<リアルDOMのみの描画の流れ>
1. HTML
2. リアルDOM
3. WEBページ仮想DOM
仮想DOMは擬似的に作られたDOM。仮想DOMの中には変更前と変更後の2つのDOMが用意されていて、この2つのDOMの差分のみがリアルDOMに反映されるようになっている。
差分のみを読み込み反映するので、描画のスピードが速い。<仮想DOMを用いた描画の流れ>
1. HTML
2. 仮想DOM →変更前のDOM & 変更後のDOM(この2つの差分のみリアルDOMへ反映)
3. リアルDOM
4. WEBページ上記の説明を踏まえて動作を確認してみる
sample.html<div id="app"> <!-- ボタンをクリックするとhandleClickメソッドが実行される --> <button @click="handleClick">カウントアップ</button> <!-- ボタンをクリックするとshowの値が反転する --> <button @click="show = !show">v-show 表示/非表示</button> <button @click="show = !show">v-if 表示/非表示</button> <!-- 値の真偽値によって要素を表示/非表示(非表示の場合display:none;の状態) --> <p ref="count" v-show="show">0<p> <!-- 値の真偽値によって要素を表示/非表示(非表示の場合DOMから要素が削除されている状態) --> <p ref="count" v-if="show">0<p> </div>sample.jsnew Vue({ el: 'app', data: { show: true }, methods: { handleClick: function() { // ref属性で紐づけたcountを変数countに代入 var count = this.$refs.count; // count(pタグ)の中のテキストを整数(10進数)にして1プラスする if(count) { count.innerText = parseInt(count.innerText, 10) + 1 } } } })上記のコードではカウントアップボタンをクリックするとpタグの中の数値が1ずつアップしていく仕組みになっている。
v-if
で表示を切り替えている部分はDOMから要素自体が削除されている(仮想DOMを切り替えている)ので、非表示にして表示し直すとpタグの数値は0
にリセットされる。
それに対しv-show
で切り替えている部分はdisplay:none;
で見えなくなっているだけで要素自体は存在している(リアルDOMには何の影響も与えていない)ので、非表示にして表示し直すとカウントアップした数値はそのまま維持される。
これらのことから$refs
と$el
の変更は一時的だということがわかる。
- 投稿日:2020-05-29T17:11:11+09:00
【Nuxt.js】firebase基礎編(Auth版):Googleログインをできるようにしよう
前置き
メールアドレスログインと
要領は同じです!
コードもこちらに付け足します✍️
https://note.com/aliz/n/n7f4ae08ba828ということで
firebaseAuthを使います?使わなくてもできますが
その場合はやることが
3倍には増えます…笑
Google Cloud Platformから
OAuthクラウドIDを発行したり
tokenの照会をするコードを書いたり??firebaseAuthを使うと
すっっごく!!!
簡単に!!!
実装できます?Step1
Googleサインインについてはこちら
firebase Google Sign-InまずはAuthentication > Sign-in method
Googleを選択メールアドレスを選択し保存するだけ!
【解説/store/index.js】
準備は整ったので
公式Contens2つめに参りましょう♪
Handle the sign-in flow with the Firebase SDK必須項目:1, 5
Optional:2, 3, 4
ということで必須項目だけ
Vuexにコピペしていきます。・actions loginGoogleを作成
ログインできたら
ログイン状態をtrueにしたいので
それを行うcheckLoginをdispatchで呼ぶ
・不要な物を削除
コメント
セミコロン(;)
var token
var userstore/index.jsimport firebase from '~/plugins/firebase' export const state = () => ({ user: { uid: '', email: '', login: false, }, }) export const getters = { user: state => { return state.user } } export const actions = { login({ dispatch }, payload) { firebase.auth().signInWithEmailAndPassword(payload.email, payload.password) .then(user => { console.log('成功!') dispatch('checkLogin') }).catch((error) => { alert(error) }) }, loginGoogle ({ dispatch }) { var provider = new firebase.auth.GoogleAuthProvider() firebase.auth().signInWithPopup(provider).then(function (result) { dispatch('checkLogin') }).catch(function (error) { console.log(error) }) }, checkLogin ({ commit }) { firebase.auth().onAuthStateChanged(function (user) { if (user) { commit('getData', { uid: user.uid, email: user.email }) commit('switchLogin') } }) }, } export const mutations = { getData (state, payload) { state.user.uid = payload.uid state.user.email = payload.email }, switchLogin (state) { state.user.login = true }, }Login.vue<template> <div class="login"> <p v-if="user.login" class="text" > {{ user }} </p> <form v-else 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> <button class="button" type="submit" @click="loginGoogle" > googleでログイン </button> </form> </div> </template> <script> export default { computed: { user () { return this.$store.getters['user'] }, }, data () { return { email: '', password: '', } }, methods : { login (email, password) { this.$store.dispatch('login', {email: this.email, password: this.password}) }, loginGoogle () { this.$store.dispatch('loginGoogle') }, } } </script>index.vue<template> <div class="page"> <Login /> <p v-if="user.login" class="text" > ログインに成功! </p> </div> </template> <script> import Login from '~/components/Login.vue' export default { components: { Login: Login, }, computed: { user () { return this.$store.getters['user'] }, }, } </script>ログインはこれだけです?
アカウント作成や、
ログアウトの仕方はまた別記事にて♪次回予告
【Nuxt.js】Nuxt文法編:v-for
6/2(火)公開予定です!
お楽しみに♪記事が公開したときにわかる様、
フォローをお願いします??
https://twitter.com/aLizlab
- 投稿日:2020-05-29T16:48:38+09:00
Vue CLIプロジェクトでコンパイル時にコードフォーマットをかける
前提
@vue/cli 4.4.1 vue create <projectName>で作成したプロジェクト
Manually select features
Linter/Formatter
にチェックPick a linter / formatter config > ESLint + Prettier
Pick additional lint features > Lint on save
lintエラーがある場合コンソールにメッセージが出るだけで
手動での修正が面倒なので、ファイル保存時に自動修正したいです。ググればVScodeの設定でやる方法がわんさかあるのですが、
今回はエディターに依存したくなかったのでwebpack経由で自動修正をします。なので、ファイル保存時ではなくコンパイル時ということになります。
また、どこからもimportされていないファイルや設定系ファイルなど、
webpackを通らないファイルに関しては自動修正されません。設定方法
vue.config.js
Vue CLIでは
vue.config.js
のconfigureWebpack
でwebpackの設定をマージできます。
Configuration Reference | Vue CLIということでプロジェクトルートに
vue.config.js
ファイルを作成して以下を記述。vue.config.jsmodule.exports = { configureWebpack: { module: { rules: [ { enforce: 'pre', test: /\.(jsx?|tsx?|vue)$/, exclude: /node_modules/, loader: 'eslint-loader', options: { fix: true } } ] } } }.eslintrc.js
デフォルトのママ
eslintrc.jsmodule.exports = { root: true, env: { node: true }, extends: [ 'plugin:vue/essential', 'eslint:recommended', '@vue/typescript/recommended', '@vue/prettier', '@vue/prettier/@typescript-eslint' ], parserOptions: { ecmaVersion: 2020 }, rules: { 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' } }.prettierrc
プロジェクトルートに
.prettierrc
ファイルを作成してお好みの設定を記述.prettierrc{ "semi": false, "arrowParens": "always", "singleQuote": true }おしまいける
以上で設定完了です。
保存前.vue<script lang="ts"> // 略 private hoge = "hoge"; // 略 </script>保存後.vue<script lang="ts"> // 略 private hoge = 'hoge' // 略 </script>これで
npm run serve
でサーバを立ち上げてる間、ファイル保存時に自動フォーマットが効いてくれます。
もちろんnpm run serve
,npm run build
実行時にも効きます。冒頭にも記載した通りwebpackを経由しないファイルは修正されないので、
lintコマンドを活用しましょう参考
- 投稿日:2020-05-29T15:35:12+09:00
[Vue.js]Object.assignの配列コピーで地味にハマったメモ
リアクティブに配列をコピーしたくて使用。
「空の配列」ひとつに対し、値を格納した配列を複数用意。
タブ切り替え時、コピーする配列を変更。test.vuelet a = [1, 2, 3, 4, 5]; let b = [6, 7, 8]; let c = [9, 10]; タブ切り替え時、コピーする配列(target部分)を変える。 Object.assign(this.array, target);その配列の要素数をviewで表示するというような実装を行ったところ、
正しい要素数が表示されずに困った。表示結果<p>{{array.length}}</P> ↓ ↓ ↓ 一番要素数が多いaの配列を格納すると、 それ以降はどのタブに切り替えても、aの要素数から変更されない原因
参考資料にもある通り、
Object.assign()メソッドはコピーを行うので、
今ある要素に上書きされる形になる。
そりゃ、要素数に変更がないわけだ・・・test.vuelet a = [1, 2, 3, 4, 5]; let b = [6, 7, 8]; let c = [9, 10]; ①aをarryにコピーする Object.assign(this.array, a); → this.arry[ 1, 2, 3, 4, 5]; ②次はbをコピーする Object.assign(this.array, b); → this.arry[ 6, 7, 8, 4, 5]; //上書きされているため、要素数は変わらない対策
配列に格納する前に、初期化処理を入れるようにした。
これで正しい要素数を表示できるようになった。test.vue// 配列初期化 this.array.splice(-this.arry.length); //コピー Object.assign(this.array, target);最後に
実際は格納した配列を子に渡すなど、処理が複雑になっているため、
こんな簡単なことに気付くのが遅れてしまいました・・・
勉強あるのみですね!!
- 投稿日:2020-05-29T15:35:12+09:00
[Vue.js]Object.assignの配列コピーで地味にハマったメモ[追記]
リアクティブに配列をコピーしたくて使用。
「空の配列」ひとつに対し、値を格納した配列を複数用意。
タブ切り替え時、コピーする配列を変更。test.vuelet a = [1, 2, 3, 4, 5]; let b = [6, 7, 8]; let c = [9, 10]; タブ切り替え時、コピーする配列(target部分)を変える。 Object.assign(this.array, target);その配列の要素数をviewで表示するというような実装を行ったところ、
正しい要素数が表示されずに困った。表示結果<p>{{array.length}}</P> ↓ ↓ ↓ 一番要素数が多いaの配列を格納すると、 それ以降はどのタブに切り替えても、aの要素数から変更されない原因
参考資料にもある通り、
Object.assign()メソッドはコピーを行うので、
今ある要素に上書きされる形になる。
そりゃ、要素数に変更がないわけだ・・・test.vuelet a = [1, 2, 3, 4, 5]; let b = [6, 7, 8]; let c = [9, 10]; ①aをarryにコピーする Object.assign(this.array, a); → this.arry[ 1, 2, 3, 4, 5]; ②次はbをコピーする Object.assign(this.array, b); → this.arry[ 6, 7, 8, 4, 5]; //上書きされているため、要素数は変わらない対策
配列に格納する前に、初期化処理を入れるようにした。
これで正しい要素数を表示できるようになった。test.vue// 配列初期化 this.array.splice(-this.arry.length); //コピー Object.assign(this.array, target);最後に
実際は格納した配列を子に渡すなど、処理が複雑になっているため、
こんな簡単なことに気付くのが遅れてしまいました・・・
勉強あるのみですね!!追記
コメントよりご指摘いただきました下記の方法でなら、
初期化処理不要で、配列のコピーができました!!
こちらの方がコードがスッキリしていいですね
slice()の参考資料追記let a = [1, 2, 3, 4, 5]; let b = [6, 7, 8]; let c = [9, 10]; ①配列aのコピーをする this.array = a.slice(); →this.array[1, 2, 3, 4, 5] ②①の後、this.arrayに配列bを再度格納する this.array = b.slice(); →this.array[6, 7, 8] //上書きではなく、コピー元の配列全体を切り出して新しく配列を生成してくれた!
- 投稿日:2020-05-29T10:38:56+09:00
【Vue.js】ナビゲーションガード(beforeRouteEnter)を使って前のページのURLを取得する
やりたいこと
VueRouterで遷移するページで、一つ前のページのURLなどを取得して、
それに応じてページ内のパーツの表示非表示を切り替えたい。beforeRouteEnterで取得しようとしたが・・・
今回はあるComponentのみで、前のURLが必要だったので、VueRouterのナビゲーションガードの
beforeRouteEnter
を使用してみました。
公式のドキュメントhogeComponent.vue<template> <div> // 前のURLに応じてここの表示非表示を切り替えたい <div v-if="prevRoute.name =='hoge'"></div> </div> </template> <script> export default { data() { return { prevRoute: null, }; }, created() { }, beforeRouteEnter(to, from, next) { next(vm => { vm.prevRoute = from; console.log(vm.prevRoute); }); }, methods: { } }; </script>これで
this.prevRoute.name
:前のページで表示していたComponentの名前
this.prevRoute.path
:前のURLが取得できます。しかし、ここで一つ問題が・・・!
nextに渡したコールバック関数はVueライフサイクルフックのmountedよりも後に実行されてしまうので、
ページを描画するタイミングではprevRoute.name
やprevRoute.path
が存在しませんよ〜
というエラーが出てしまう。。。。
beforeRouteEnterのnextコールバックの実行タイミングはこちらの記事を参考にさえていただきました。
ちなみに、コンポーネント描画後であれば、this.prevRoute.name
などで取得できます!Vuexを使ってみる
そこで、
beforeRouteEnter
で取得した前のページのデータをVuexのStateで管理し、
computed
でVuexのStateからデータを取得するという方法をとる事にしました。
もっと簡単な方法があればぜひ教えてください・・・!hogeComponent.vue<script> //省略 beforeRouteEnter(to, from, next) { // ここでVuexに前のページの情報をStateに渡す!! next(vm => { vm.prevRoute = from; }); } </script>実装方法
hogeComponent.vue<script> import store from "../../../store/index"; import { mapMutations, mapGetters } from "vuex"; export default { data() { return { prevRoute: null }; }, beforeRouteEnter(to, from, next) { store.commit("route/setPrevRoute", from); next(vm => { vm.prevRoute = from; }); }, computed: { prevRouteChild() { return this.$store.getters["route/getPrevRoute"]; } } }; </script>store/modules/route.jsconst state = { prevRoute: [] }; const mutations = { setPrevRoute(state, from) { state.prevRoute = from; } }; const actions = {}; const getters = { getPrevRoute: state => state.prevRoute }; export default { namespaced: true, state, mutations, getters, };store/index.jsimport Vue from "vue"; import Vuex from "vuex"; import route from "./modules/route"; Vue.use(Vuex); export default new Vuex.Store({ modules: { route } });これで初期描画のタイミングで表示非表示を切り替えることができました!
何か間違ってたら、是非教えてください^^参考ページ
- 投稿日:2020-05-29T03:59:04+09:00
天界の様子が見れる秘密のサイトつくった。
秘密だから特定の人しか見れないようにしてます
サイト:www.tenkai.ml
[Emall:demo@demo.jp Pass:demo00 でログインできます。(怖くないよ)]ログインすると...
天界の様子(大阪エリア)が確認できます。
えぇ...。天界とは、天気です。天界(大阪エリア)の窓をスクロールすると...
猫。
(天気によって、出てくる猫が変わります。ぜひ、晴れの日にサイトを覗きにきてください。)サイトの仕組み
1.最初のログイン画面は Firebase Authentication 使ってます。
参考にさせていただいた記事->https://qiita.com/shima-07/items/2c344b0ad306b201a0652.天界(大阪エリア)の情報は OpenWeatherMap の API を取得しています。
参考にさせていただいた記事->https://www.tam-tam.co.jp/tipsnote/html_css/post16405.html3.天界(大阪エリア)の小窓は、html を、もう一つデプロイして、それを埋め込んでいます。
参考にさせていただいた記事->https://gray-code.com/html_css/view-another-html-on-current-html/4.サイトのデプロイは Netlify というサービスを使いました。
参考にさせていただいた記事->https://qiita.com/oganyanATF/items/7fb681e863d8681c9039
(Netlify で html をデプロイする時は、index.html の名称でないと、うまくデプロイできないので気をつけてください。)おまけ
今回のサイトの GitHub を公開します。
・メインサイト(ログイン機能含む)のリポジトリ->https://github.com/cazmura/ten-kai-login
・埋め込みサイトのリポジトリ->https://github.com/cazmura/ten-kai最後まで読んでいただきありがとうございました。
- 投稿日:2020-05-29T03:35:30+09:00
Vue.jsとFirebaseを併用したらDataの取り扱いに困った話
表題の通りです。
Vue.jsとGoogleのFirebaseを利用して画像を保存するモノを作ってる時に、全然うまくいかなかったので、
その備忘録として残しておきます。
あとで思い返したら単純に書き方が間違ってただけなんですが。。
初心者なので暖かめの目で多めにみて頂けたらと思います。
おかしいところとかあればぜひご指摘ください。環境
Mac OS X Catalina 10.15.4
Vue.js 2.6.11
Vue CLI 4.3.1
AWS Cloud9 Firebaseやりたい事
買ったコーヒーをメモるためのアプリを作ろうとしてました。
Firebaseに画像を保存し、画面読み込みor更新した際に画面に表示させたい。
その最低限の準備です。保存の実装
事前にVue CLIにFirebaseを読み込ませます。
yarn add firebase --save もしくはこちらで npm install firebase --saveFirebaseの設定はいろいろありますが、こちらの記事を参考に爆速で構築させて頂きました。
Vue.js + Firebase を使って爆速でユーザ認証を実装するトップ画面に、firebaseに保存した画像を表示させるため、まずは保存します。
CoffeeRegister.vue<template> <div class="back"> <div class="register"> <h2>Coffee Register</h2> <!--名前とか買った店、写真を保存する--> <input type="text" placeholder="CoffeeName" v-model="coffeeName"> <input type="text" placeholder="ShopName" v-model="shopName"> <input type="file" v-on:change="onFileChange" id="coffee-image" accept="image/jpeg,image/png,image/gif"> <!--保存ボタン 後述のrecordメソッドで保存--> <button v-on:click="record" class="recordbtn">Record</button> <!--保存せずに戻るボタン--> <button v-on:click="toIndex" class="returnbtn">Return</button> </div> </div> </template> <script> import firebase from 'firebase' //firebase読み込み export default { name: 'CoffeeRegister', data: function(){ return { coffeeName: '', shopName: '', file: null, // 選択した画像を持っておく変数 coffeeImageLocation: null, //画像を保存する場所のURLを保存する変数 }; }, methods: { toIndex: function() { //トップページに戻るメソッド this.$emit('return-click-register', (false)); }, // ここから画像ファイルを保存するメソッド onFileChange: function(e) { const image = e.target.files; //選択された画像ファイルを選択 this.file = image[0]; //画像ファイルを1つだけ選択 // Firebase storageに保存するパスを決める this.coffeeImageLocation = `coffee-images/${this.coffeeName}`; }, // firebase storageに保存するメソッド record: function() { //画像をfirebase storageに保存 firebase .storage() .ref(this.coffeeImageLocation) //さっき決めたパスを参照して、 .put(this.file) //保存する .then(() => { //保存が成功したら、保存した画像ファイルの場所とともにfirebase databaseに保存する準備 const coffeeData = { name: this.coffeeName, shop: this.shopName, coffeeImageLocation: this.coffeeImageLocation, createdAt: firebase.database.ServerValue.TIMESTAMP, }; // ここでfirebase databaseに保存する firebase .database() .ref('beans') //保存する場所を参照して、 .push(coffeeData) //追加で保存 setメソッドを使うと上書きされる .then(() => { console.log('saved coffee data.'); alert('data is registration.'); this.$emit('success-coffee-registration', false); //親コンポーネントに伝達 }) .catch((error) => { console.error('cannnot saved.', error); }); }) .catch((error) => { console.error('image uproad error.', error); }); } } } </script>firebaseには独特のメソッドがあるし、JSとかと似てるからちょっと困る時がある。
メインページで保存したデータ読み込み
次にトップ画面で読み込みを実装します。
Index.vue<template> ..省略.. <div class="coffee-item"> <div class="coffee-image-wrapper"> <img class="coffee-item-image" alt="" v-bind:src="image"> </div> <div class="coffee-detail"> <div class="coffee-item-name">コーヒー</div> <div class="coffee-item-delete-wrapper"> <button class="btn btn-danger coffee-item-delete">削除</button> </div> </div> </div> ..省略.. </template> <script> ..省略.. import CoffeeRegister from '@/components/CoffeeRegister.vue'; export default { ... name: 'Index', components: { ... CoffeeRegister, }, data: function () { return { ... lct: '', image: '', }; }, created: function() { ... this.loadCoffeeView(); this.downloadCoffeeImages(this.lct); }, methods: { // firebase databaseからコーヒーデータをダウンロード loadCoffeeView: function() { const coffeeRef = firebase .database() .ref('beans') //firebase database の beans に保存したデータを参照 .orderByChild('createdAt'); //並び替え // 過去に登録したイベントハンドラを削除 coffeeRef.off('child_added'); // ここで保存データを抜き取り coffeeRef.on('child_added',(coffeeSnapshot) => { const coffeeId = coffeeSnapshot.key; const coffeeData = coffeeSnapshot.val(); this.lct = coffeeData.coffeeImageLocation; //ここでURLが抜き取られる }); }, //firebase storageからコーヒーの画像データをダウンロード downloadCoffeeImages: function(coffeeImageLocation) { firebase .storage() .ref(coffeeImageLocation) .getDownloadURL() .then((data) => { console.log(data); this.image = data; }) .catch((error) => { console.error('cannot download coffee images.', error); }); }, }; }; </script>全体像としては上記なのですが、要はライフサイクルフックのcreatedで画像やらデータを読み込んで表示させたかったんです。
Index.vueの抜粋data: function () { return { ... lct: '', image: '', }; created: function() { ... // ここのメソッドで画像URLを[ lct ]に格納して、 this.loadCoffeeView(); // ここで画像を抜き取って[ image ]に格納して、v-bindで表示させる予定 this.downloadCoffeeImages(this.lct); },エラー内容
めっちゃ抽象的で困りました。
createdフックでエラーなのはわかります。試した事
じゃcreatedじゃなかったらいいの?と思ったので、
mountedで記述したんですが、全く同じエラー。
単体で確認しようと思い、v-on:clickディレクティブ使って確認したらちゃんと動いてました。なんで単体で動くのにライフサイクルフックで動かないの?
と思って、データ抽出メソッドをcreatedにして、URL抽出メソッドをmountedにしてみたら、
mountedで同様のエラーが出ました。
ここのどこかが悪いってわかったので、いろいろ試してみます。//firebase storageからコーヒーの画像データをダウンロード downloadCoffeeImages: function(coffeeImageLocation) { firebase .storage() .ref(coffeeImageLocation) .getDownloadURL() .then((data) => { console.log(data); this.image = data; }) .catch((error) => { console.error('cannot download coffee images.', error); }); },いろいろ試したら、
.ref(coffeeImageLocation)
のところで何かがおかしいみたい。
typeof
で、このメソッドに渡される予定の変数がどうなってるか確認したら、undefined。結果
結論からいうと、前述の画像URLを取得して、変数に格納する前に画像参照するメソッドが動いてたから表示されてなかったみたいです。
Index.vue<script> ... // firebase databaseからコーヒーデータをダウンロード loadCoffeeView: function() { const coffeeRef = firebase .database() .ref('beans') .orderByChild('createdAt'); // 過去に登録したイベントハンドラを削除 coffeeRef.off('child_added'); coffeeRef.on('child_added',(coffeeSnapshot) => { const coffeeId = coffeeSnapshot.key; const coffeeData = coffeeSnapshot.val(); console.log(coffeeData); const location = coffeeData.coffeeImageLocation; console.log('coffee data is:', typeof location); console.log(location); //firebase storageからコーヒーの画像データをダウンロード firebase .storage() .ref(location) .getDownloadURL() .then((data) => { console.log('data is: ', data); this.image = data; }) .catch((error) => { console.error('cannot download coffee images.', error); }); }); }, ... </script>最終的に全部同じメソッドに記述したらちゃんと表示されました。
変にdataオブジェクトに格納しない方がいいんですね。このへんの事調べてもよく分からなかったので、ご存知の方がいましたら
ぜひ教えて頂けませんか?参考