- 投稿日:2019-11-29T15:57:52+09:00
Vue.js を使うときに気を付けること。
Vue.js を使うときに気を付けること。
とりあえず、今の時点で痛い目にあったもの。
文字列の表示は .nodeValue を使っているので改行が無くなる。
<div>{{text}}</div>
のような場合に、this.text に改行が入っていても、その改行は無視されて一行で表示されてしまう。.wrap { white-space:pre-wrap; word-wrap:break-word; }
のようなものを用意して
<div class="wrap">{{text}}</div>
とする。VueRouter では同じハッシュ値だと一切何のアクションも発生しない。
各ページへのリンクを上部において、本体を下部に表示している場合に、
最新の情報にしたく更新のつもりで再度同じ<router-link>
をクリックしても Vue は一切反応してくれない。
※ createdフックなどが呼ばれない、というか何も処理が動かない。ただ、パラメータが違う場合は、beforeRouteUpdate フックが呼ばれるようなので、
面倒だがクリック毎に変化するダミーを用意してこのフックで捕捉して処理する。
- 投稿日:2019-11-29T14:49:08+09:00
Vue.jsで連想配列を使おう!
?Vue.jsで連想配列を使おう!
はじめまして、s4na?と申します。
今回はじめてQiitaの記事を書きます。今回書く内容は、少し前にVue.jsで実装していたら躓いた連想配列の利用方法です。
一時期は「計算量が増えるけど、連想配列を使わないで実装してしまおうかな・・・?」と思ったこともあるくらい大きく躓きました。少しでも私と同じところで躓く人の力になればと思い、今回筆をとりました。?
RailsのViewからVue.jsに連想配列を渡す方法
まずはRailsのViewからVue.jsに連想配列を渡す方法について書いていきます。
この方法は、Rails側からVue.jsに初期値を渡す際に使います。
もちろん、APIを作ってそちらで渡してもいいのですが、それだとAPI分の通信量が増えてしまうので、私はViewから渡しています。
- 補足:RailsのView = MVCのView
ControllerでJSON化しておく
- 補足:Hashのまま渡すと、Vue.jsがParseできないです。フロントで無理にパースしてもいいのかもしれませんが、サーバーサイドでできることはサーバーサイドでしたほうが良さそうです
@foo = JSON(foo)View
- 補足:
- RailsのTemplate Engineはslimで書いています
@foo
,@baa
みたいに値を複数渡す場合もあるので、#js-el-data-foo
のように名前を区切ると便利です。#js-el # ?Vue.jsがマウントするDOM #js-el-data-foo = @foo # ?渡したい値 その1 #js-el-data-baa = @baa # ?渡したい値 その2Vueインスタンス
Vue.js側では、ViewのDOMの
innerText
を取得し、JSON化してdata
に入れ込みます。<script> export default { data: () => { return { reservations: {} } }, created: function() { // ?JSONが連想配列になる ?JSONで受け取る this.memos = JSON.parse(document.querySelector('#js-el-data-foo').innerText); } } </script>Vueインスタンスで連想配列をループ表示する方法
連想配列の表示は
連想配列[key]
とする必要があり、そのためにはkey
の集合であるkeys
が必要になります。
- 補足:テンプレートエンジンには
Pug
を使用しています<template lang="pug"> .foo(v-for="key in keys") | {{ foo[key] }} </template> <script> export default { data: () => { return { reservations: {}, keys: [] } } } </script>Vueインスタンス内で連想配列に情報を追加する方法
追加する際は
$set
メソッドを使いましょう。これは書こうとした時、めっちゃハマりました。?
この記事を書こうと思った原点でもあります。他の言語やフレームワークを利用していたりすると、「
val['key']
で値を代入できるのでは?」と考えてしまうこともあるかと思います。(私はそうでした。)Vue.jsでは、JavaScriptの仕様で、連想配列に要素を追加した時に検知できません。
なので、変更検出が可能なメソッドを利用する必要があります。(公式ドキュメントを読めば気づけるけど、読んでなかった?)export default { data: () => { return { foo: {} } }, methods: { addFoo: function(key, hoge) { this.$set(this.foo, `key`, hoge); } }連想配列から要素を削除する方法
削除する際は
$delete
メソッドを使いましょう。
JavaScriptを書いていると、delete
メソッドで削除してしまったりすることがあると思います。(私はそうでした)
それだと$set
と同様に、Vue.jsが要素数の変化を検知できないので、$delete
を使う必要があります。methods: { deleteFoo: function(key) { this.$delete(this.foo, key); } }Vueインスタンスで連想配列をAPIで取得する方法
methods: { token () { const meta = document.querySelector('meta[name="csrf-token"]') return meta ? meta.getAttribute('content') : '' }, getFoo: function() { fetch(`/api/hogehoge`, { method: 'POST', headers: { 'Content-Type': 'application/json; charset=utf-8', 'X-Requested-With': 'XMLHttpRequest', 'X-CSRF-Token': this.token() }, credentials: 'same-origin', redirect: 'manual', }) .then(response => { return response.json(); }) .then(json => { this.$set(this.foo, `key`, json); // ?受け取った値を連想配列`foo`に入れる }) .catch(error => { console.warn('Failed to parsing', error); }) },まとめ
- Vue.jsで連想配列を扱うのは、癖がわかれば結構簡単!
- 時間がなくて厳しくても、触ったことのない技術を触るのであれば、何よりもまずは公式ドキュメントを全部読もう!!!
- Vue.jsならこちらのガイドを読もう!!!
- 投稿日:2019-11-29T13:29:28+09:00
Firestoreのデータを可視化・監視しアラートする、かんたんなWEBアプリ(Nuxt.js,GCP Firestore)
はじめに
Nuxt.jsの勉強がてら、Firestoreに格納されているデータをブラウザに表示させつつ、リアルタイムに変更があった際に、その更新が反映される簡易WEBアプリを作成しました。
アクションとしては、3つの仕組みを用意しました。
①トグルボタンでのFirestoreデータ更新(True/False)
②削除ボタンでのFirestoreデータ削除
③更新(追加、変更、削除)があった際、ブラウザ上アラートが上がる作成したNuxt.jsプロジェクトは、Firebase Hostingにデプロイして動作確認し、
手動デプロイができたので、GCP CloudBuildを使って、CIの仕組みも少し試験しました。見た目はこんなものです。(CSSについては特に今回は言及しません。)
このカラム名で、データがドキュメント毎に保存(図にあるのは二つ)されている形になります。技術要素
- Firestore
- vue.js / Nuxt.js
- Firebase Hosting
- GCP CloudBuild
説明
- 1. create-nuxt-appでNuxt.jsプロジェクト作成
- 2. Firestoreへ接続するコード準備
- 3. Storeの設定
- 4. 表示部分のページ作成
- 5. ローカルで動作確認
- 6. Firebase Hostingへデプロイ
- 7. CloudBuildでCI環境構築
1. create-nuxt-appでNuxt.jsプロジェクト作成
まず、SEOなどは気にせず、今回はSPA(Single Page Application)でお手軽に作成したいので、SPAを選択しました。
参考サイトは、Nuxt.jsを使うときに、SPA・SSR・静的化のどれがいいか迷ったらです。基本的にはデフォルトで、SPAを選択する箇所のみ変更。
npx create-nuxt-app nuxt-view-firestore-status必要に応じて、npmパッケージはインストールしておきます。後々利用します。
npm install --save firebase npm install --save vuexfire2. Firestoreへ接続するコード準備
必須ではないですが、dotenvを使って環境変数にプロジェクトIDと後々使うFirestoreのコレクション名を格納しようと思います。nuxt.config.jsのmodulesに、にdotenvを設定。
そして、.envファイルは、作成されたフォルダ直下に新規作成。nuxt.config.jsmodules: [ '@nuxtjs/dotenv' ],.envFIREBASE_PROJECT_ID = '<your project id>' FIRESTORE_COLLECTION_NAME = '<your firestore collection name>'次に、Firestoreの初期コンフィグを定義する、Vueプラグインを作成します。pluginsフォルダ配下に、firebase.jsを作成します。
plugins/firebase.jsimport firebase from 'firebase' const config = { projectId: process.env.FIREBASE_PROJECT_ID } if (!firebase.apps.length){ firebase.initializeApp(config) } export default firebase3. Storeの設定
今回は、ストアの利用方法の「クラシックモード」と「モジュールモード」のうち、モジュールモードで作成します。構成は以下です。
Nuxt.jsのストアをモジュールモードで使用するときのTips
Nuxt.js + Firebase で vuexfire を使って Cloud Firestore と連携する(データバインド編)
store ├── index.js └── view_status.jsindex.jsでは、vuexfire の vuexfireMutations を定義するのみ。
store/index.jsimport { vuexfireMutations } from 'vuexfire' export const mutations ={ ...vuexfireMutations }次に、view_status.jsに、Firestoreの読み込みと、ステート、アクション定義。
アクション名 説明 init Firestoreから取得するデータをバインド remove 削除ボタンが押されたときに呼び出す。ドキュメント名を引数にFirestoreのドキュメント削除 toggle トグルボタンが押されたときに呼び出す。対象ドキュメントのkeyがdoneの値をTrue/Falseへ変更させる。(Todosサンプルアプリの流用なのであまり気にしないでください。) popup Firestoreの対象Collection内のドキュメントが追加、削除、更新が発生した際に、アラートを上げる機能 store/view_status.jsimport firebase from '~/plugins/firebase' import { firestoreAction } from 'vuexfire' const db = firebase.firestore() const statusRef = db.collection(process.env.FIRESTORE_COLLECTION_NAME) export const state = () => ({ statuses: [] }) export const actions = { init: firestoreAction(({ bindFirestoreRef }) => { bindFirestoreRef('statuses', statusRef) }), remove: firestoreAction((context, id) => { statusRef.doc(id).delete() }), toggle: firestoreAction((context, status) => { statusRef.doc(status.id).update({ done: !status.done }) }), popup: firestoreAction((context) => { statusRef.onSnapshot(function(snapshot){ snapshot.docChanges().forEach(function(change){ if (change.type === "added"){ alert("ドキュメントが追加されました。\r\nIDは、" + change.doc.id + "です。") } if (change.type === "modified"){ alert("ドキュメントが変更されました。\r\nIDは、" + change.doc.id + "です。") } if (change.type === "removed"){ alert("ドキュメントが削除されました。\r\nIDは、" + change.doc.id + "です。") } }) }) }) }注意
FirestoreのCollectionのルール設定は、今回、開けてテストを実施しましたが、本番利用時はセキュリティ設計の考慮が必要です。FirebaseコンソールのDatabase -> 「ルール」でGUIで設定可能です。
Firebaseのサイトの安全でないルールを修正するを参照。
match /<your collection name>/{document=**} { allow read, write: if true; }4. 表示部分のページ作成
最後に、表示部分のページ作成するため、pagesフォルダに、閲覧用のVueファイルを作成します。status.vueとしました。
script 説明 created ページが開いた時の処理。Vue インスタンスが生成された直後に呼び出されます。今回は、上記で定義した、initとpopupアクションを呼び出しています。 methods vueテンプレートから呼び出すメソッドで、remove(id)とtoggle(status)を定義しました。上記で定義したremoveとtoggleアクションを引数設定しつつ呼び出します。 computed 値が変わると再評価される仕組みで、今回はstoreの値を見て変化があれば更新されるようにしています。 pages/status.vue(抜粋)created: function() { this.$store.dispatch('view_status/init') this.$store.dispatch('view_status/popup') }, methods: { remove(id) { this.$store.dispatch('view_status/remove', id) }, toggle(status) { this.$store.dispatch('view_status/toggle', status) }, }, computed: { view_status() { return this.$store.getters['view_status/orderedStatus'] } },テーブルで取得したFirestoreのデータを表示しています。
pages/status.vue(抜粋)<template> <div> <table> <tr> <th>-</th> <th>done</th> <th>session_id</th> <th>url</th> <th>timestamp</th> <th>remove</th> </tr> <tr v-for="status in view_status" :key="status.id"> <td> <input type="checkbox" v-bind:checked="status.done" @change="toggle(status)"> </td> <td>{{status.done}}</td> <td> {{status.session_id}}</td> <td> <a v-bind:href="status.url">{{status.url}}</a></td> <td> {{status.timestamp.toDate() | dateFilter}}</td> <td> <button v-on:click="remove(status.id)">remove</button> </td> </tr> </table> </div> </template>簡単にコメントすると、
pages/status.vue(抜粋)<tr v-for="status in view_status" :key="status.id">view_statusで取得した値(=ドキュメント一覧)をドキュメント毎に、v-forで繰り返し処理しています。
pages/status.vue(抜粋)<input type="checkbox" v-bind:checked="status.done" @change="toggle(status)">UI上、一番左のカラムにチェックボックスを配置し、チェックON/OFFは、ドキュメント(status)のdoneの値をバインドしています。変更があった場合は、toggleメソッドを呼び出します。
pages/status.vue(抜粋)<a v-bind:href="status.url">{{status.url}}</a>ドキュメント(status)のurlの値にURLの文字列が格納されているのですが、最初はmustacheタグ{{status.url}}で表示させようとしましたが、それだとリンクがつかないので、aタグでリンクさせたい場合は、このようにバインドさせて表示させる必要があるようです。
pages/status.vue(template内)<td> {{status.timestamp.toDate() | dateFilter}}</td>pages/status.vue(script内)import moment from 'moment' //(省略) filters: { dateFilter: function(date) { return moment(date).format('YYYY/MM/DD HH:mm:ss') } }タイムスタンプの値を、指定の形式で表示させようとする場合は、フィルタ機能を使って実現させると良いようです。momentライブラリを使いました。
pages/status.vue(抜粋)<td> <button v-on:click="remove(status.id)">remove</button> </td>timestampを降順にする
computedのところでは、storeのgettersを呼び出していますが、timestampの順序を変える仕組みを導入しました。日付でソートをかけるために、webpackプラグイン(lodash)をinstallしています。
webpack プラグインを追加するには?と、細かすぎるけど伝わって欲しいlodash.jsの話を参照。
nuxt.config.jsconst webpack = require('webpack') // (省略) build: { /* ** You can extend webpack config here */ extend (config, ctx) { }, plugins: [ new webpack.ProvidePlugin({ '_': 'lodash' }) ] }そのうえで、gettersをview_status定義し、降順でtimestampが並ぶように処理をします。
store/view_status.jsexport const getters = { orderedStatus: state => { return _.orderBy(state.statuses, 'timestamp', 'desc') } }5. ローカルで動作確認
ローカルでの検証は
npm run dev
で動作させ、localhost:3000/status上で正常に動くことを確認しました。再掲になりますが、以下のようなUIで、動作が問題なく動きました。
+ 一番左のチェックボックスをクリックすると、Doneの値がtrueへ更新
+ 再度クリックするとDoneの値がfalseに更新
+ 一番右のremoveボタンをクリックすると対象データが削除
+ 上記アクションを実施した際に、アラートが画面上上がること6. Firebase Hostingへのデプロイ
まず、Nuxtプロジェクトをビルドします。
npm run build
Firebaseプロジェクトをfirebase init
コマンドで初期化します。注意としては、hostingを選択することと、build後に生成されるフォルダはdist(not public)なので、distを指定することです。
上記を実施後、
firebase deploy
コマンドでデプロイします。
問題がなければ、https://<your project>.firebaseapp.com/status/
などにアクセスして確認できます。7. CloudBuildでCI環境構築
CloudBuildでCI環境構築構築し、githubにコードをデプロイすると、CloudBuildがトリガーされ、FirebaseHostingへ自動デプロイの仕組みも作りました。CloudBuild側の設定は、別途サイトを参考ください。
準備としては、Dockerfileを作成し、cloudbuild.yamlで設定を入れることで動作しました。
ちなみに、.gitignore
ファイルに.env
が記載されているので、CloudBuildで連携する場合は、.envもリポジトリに上げるようにする必要があります。Dockerfile# use latest Node 10 FROM node:10 # install Firebase CLI RUN npm install -g firebase-tools ENTRYPOINT ["/usr/local/bin/firebase"]steps: # Install - name: 'gcr.io/cloud-builders/npm' args: ['install'] # Build - name: 'gcr.io/cloud-builders/npm' args: ['run', 'build'] # Docker Image - name: 'gcr.io/cloud-builders/docker' args: ['build', '-t', 'gcr.io/$PROJECT_ID/firebase', '.'] # Deploy - name: 'gcr.io/$PROJECT_ID/firebase' args: ['deploy', '--project', '$PROJECT_ID'] # push built images to Container Registry images: ['gcr.io/$PROJECT_ID/firebase']おわりに
Vue.js / Nuxt.js のおかげで比較的簡単に、ブラウザ上でFirestoreデータを確認し、更新があったらアラートが上がる機能を作成できました。
もう少し応用的なものも作ってみたいと思うのと、環境面では、FirebaseHostingではなく、Google App Engineにもデプロイができるようなので、そちらも試してみたいと思います。
参考コンテンツ
- Udemy 中村祐太「Nuxt JS入門決定版!Vue.jsのフレームワークNuxt JSの基本からFirebaseと連携したSPAの開発まで」
- Nuxt.js プロジェクトを Firebase Hosting にデプロイして公開する方法
- Nuxt.jsを使うときに、SPA・SSR・静的化のどれがいいか迷ったら
- Nuxt.jsのストアをモジュールモードで使用するときのTips
- Nuxt.js + Firebase で vuexfire を使って Cloud Firestore と連携する(データバインド編)
- 細かすぎるけど伝わって欲しいlodash.jsの話
- 投稿日:2019-11-29T13:29:28+09:00
Firestoreのデータを可視化・監視する、かんたんなWEBアプリ(Nuxt.js,GCP Firestore)
はじめに
Nuxt.jsの勉強がてら、Firestoreに格納されているデータをブラウザに表示させつつ、リアルタイムに変更があった際に、その更新が反映される簡易WEBアプリを作成しました。
アクションとしては、3つの仕組みを用意しました。
①トグルボタンでのFirestoreデータ更新(True/False)
②削除ボタンでのFirestoreデータ削除
③更新(追加、変更、削除)があった際、ブラウザ上アラートが上がる作成したNuxt.jsプロジェクトは、Firebase Hostingにデプロイして動作確認し、
手動デプロイができたので、GCP CloudBuildを使って、CIの仕組みも少し試験しました。見た目はこんなものです。(CSSについては特に今回は言及しません。)
このカラム名で、データがドキュメント毎に保存(図にあるのは二つ)されている形になります。技術要素
- Firestore
- vue.js / Nuxt.js
- Firebase Hosting
- GCP CloudBuild
説明
- 1. create-nuxt-appでNuxt.jsプロジェクト作成
- 2. Firestoreへ接続するコード準備
- 3. Storeの設定
- 4. 表示部分のページ作成
- 5. ローカルで動作確認
- 6. Firebase Hostingへデプロイ
- 7. CloudBuildでCI環境構築
1. create-nuxt-appでNuxt.jsプロジェクト作成
まず、SEOなどは気にせず、今回はSPA(Single Page Application)でお手軽に作成したいので、SPAを選択しました。
参考サイトは、Nuxt.jsを使うときに、SPA・SSR・静的化のどれがいいか迷ったらです。基本的にはデフォルトで、SPAを選択する箇所のみ変更。
npx create-nuxt-app nuxt-view-firestore-status必要に応じて、npmパッケージはインストールしておきます。後々利用します。
npm install --save firebase npm install --save vuexfire2. Firestoreへ接続するコード準備
必須ではないですが、dotenvを使って環境変数にプロジェクトIDと後々使うFirestoreのコレクション名を格納しようと思います。nuxt.config.jsのmodulesに、にdotenvを設定。
そして、.envファイルは、作成されたフォルダ直下に新規作成。nuxt.config.jsmodules: [ '@nuxtjs/dotenv' ],.envFIREBASE_PROJECT_ID = '<your project id>' FIRESTORE_COLLECTION_NAME = '<your firestore collection name>'次に、Firestoreの初期コンフィグを定義する、Vueプラグインを作成します。pluginsフォルダ配下に、firebase.jsを作成します。
plugins/firebase.jsimport firebase from 'firebase' const config = { projectId: process.env.FIREBASE_PROJECT_ID } if (!firebase.apps.length){ firebase.initializeApp(config) } export default firebase3. Storeの設定
今回は、ストアの利用方法の「クラシックモード」と「モジュールモード」のうち、モジュールモードで作成します。構成は以下です。
Nuxt.jsのストアをモジュールモードで使用するときのTips
Nuxt.js + Firebase で vuexfire を使って Cloud Firestore と連携する(データバインド編)
store ├── index.js └── view_status.jsindex.jsでは、vuexfire の vuexfireMutations を定義するのみ。
store/index.jsimport { vuexfireMutations } from 'vuexfire' export const mutations ={ ...vuexfireMutations }次に、view_status.jsに、Firestoreの読み込みと、ステート、アクション定義。
アクション名 説明 init Firestoreから取得するデータをバインド remove 削除ボタンが押されたときに呼び出す。ドキュメント名を引数にFirestoreのドキュメント削除 toggle トグルボタンが押されたときに呼び出す。対象ドキュメントのkeyがdoneの値をTrue/Falseへ変更させる。(Todosサンプルアプリの流用なのであまり気にしないでください。) popup Firestoreの対象Collection内のドキュメントが追加、削除、更新が発生した際に、アラートを上げる機能 store/view_status.jsimport firebase from '~/plugins/firebase' import { firestoreAction } from 'vuexfire' const db = firebase.firestore() const statusRef = db.collection(process.env.FIRESTORE_COLLECTION_NAME) export const state = () => ({ statuses: [] }) export const actions = { init: firestoreAction(({ bindFirestoreRef }) => { bindFirestoreRef('statuses', statusRef) }), remove: firestoreAction((context, id) => { statusRef.doc(id).delete() }), toggle: firestoreAction((context, status) => { statusRef.doc(status.id).update({ done: !status.done }) }), popup: firestoreAction((context) => { statusRef.onSnapshot(function(snapshot){ snapshot.docChanges().forEach(function(change){ if (change.type === "added"){ alert("ドキュメントが追加されました。\r\nIDは、" + change.doc.id + "です。") } if (change.type === "modified"){ alert("ドキュメントが変更されました。\r\nIDは、" + change.doc.id + "です。") } if (change.type === "removed"){ alert("ドキュメントが削除されました。\r\nIDは、" + change.doc.id + "です。") } }) }) }) }注意
FirestoreのCollectionのルール設定は、今回、開けてテストを実施しましたが、本番利用時はセキュリティ設計の考慮が必要です。FirebaseコンソールのDatabase -> 「ルール」でGUIで設定可能です。
Firebaseのサイトの安全でないルールを修正するを参照。
match /<your collection name>/{document=**} { allow read, write: if true; }4. 表示部分のページ作成
最後に、表示部分のページ作成するため、pagesフォルダに、閲覧用のVueファイルを作成します。status.vueとしました。
script 説明 created ページが開いた時の処理。Vue インスタンスが生成された直後に呼び出されます。今回は、上記で定義した、initとpopupアクションを呼び出しています。 methods vueテンプレートから呼び出すメソッドで、remove(id)とtoggle(status)を定義しました。上記で定義したremoveとtoggleアクションを引数設定しつつ呼び出します。 computed 値が変わると再評価される仕組みで、今回はstoreの値を見て変化があれば更新されるようにしています。 pages/status.vue(抜粋)created: function() { this.$store.dispatch('view_status/init') this.$store.dispatch('view_status/popup') }, methods: { remove(id) { this.$store.dispatch('view_status/remove', id) }, toggle(status) { this.$store.dispatch('view_status/toggle', status) }, }, computed: { view_status() { return this.$store.getters['view_status/orderedStatus'] } },テーブルで取得したFirestoreのデータを表示しています。
pages/status.vue(抜粋)<template> <div> <table> <tr> <th>-</th> <th>done</th> <th>session_id</th> <th>url</th> <th>timestamp</th> <th>remove</th> </tr> <tr v-for="status in view_status" :key="status.id"> <td> <input type="checkbox" v-bind:checked="status.done" @change="toggle(status)"> </td> <td>{{status.done}}</td> <td> {{status.session_id}}</td> <td> <a v-bind:href="status.url">{{status.url}}</a></td> <td> {{status.timestamp.toDate() | dateFilter}}</td> <td> <button v-on:click="remove(status.id)">remove</button> </td> </tr> </table> </div> </template>簡単にコメントすると、
pages/status.vue(抜粋)<tr v-for="status in view_status" :key="status.id">view_statusで取得した値(=ドキュメント一覧)をドキュメント毎に、v-forで繰り返し処理しています。
pages/status.vue(抜粋)<input type="checkbox" v-bind:checked="status.done" @change="toggle(status)">UI上、一番左のカラムにチェックボックスを配置し、チェックON/OFFは、ドキュメント(status)のdoneの値をバインドしています。変更があった場合は、toggleメソッドを呼び出します。
pages/status.vue(抜粋)<a v-bind:href="status.url">{{status.url}}</a>ドキュメント(status)のurlの値にURLの文字列が格納されているのですが、最初はmustacheタグ{{status.url}}で表示させようとしましたが、それだとリンクがつかないので、aタグでリンクさせたい場合は、このようにバインドさせて表示させる必要があるようです。
pages/status.vue(template内)<td> {{status.timestamp.toDate() | dateFilter}}</td>pages/status.vue(script内)import moment from 'moment' //(省略) filters: { dateFilter: function(date) { return moment(date).format('YYYY/MM/DD HH:mm:ss') } }タイムスタンプの値を、指定の形式で表示させようとする場合は、フィルタ機能を使って実現させると良いようです。momentライブラリを使いました。
pages/status.vue(抜粋)<td> <button v-on:click="remove(status.id)">remove</button> </td>timestampを降順にする
computedのところでは、storeのgettersを呼び出していますが、timestampの順序を変える仕組みを導入しました。日付でソートをかけるために、webpackプラグイン(lodash)をinstallしています。
webpack プラグインを追加するには?と、細かすぎるけど伝わって欲しいlodash.jsの話を参照。
nuxt.config.jsconst webpack = require('webpack') // (省略) build: { /* ** You can extend webpack config here */ extend (config, ctx) { }, plugins: [ new webpack.ProvidePlugin({ '_': 'lodash' }) ] }そのうえで、gettersをview_status定義し、降順でtimestampが並ぶように処理をします。
store/view_status.jsexport const getters = { orderedStatus: state => { return _.orderBy(state.statuses, 'timestamp', 'desc') } }5. ローカルで動作確認
ローカルでの検証は
npm run dev
で動作させ、localhost:3000/status上で正常に動くことを確認しました。再掲になりますが、以下のようなUIで、動作が問題なく動きました。
+ 一番左のチェックボックスをクリックすると、Doneの値がtrueへ更新
+ 再度クリックするとDoneの値がfalseに更新
+ 一番右のremoveボタンをクリックすると対象データが削除
+ 上記アクションを実施した際に、アラートが画面上上がること6. Firebase Hostingへのデプロイ
まず、Nuxtプロジェクトをビルドします。
npm run build
Firebaseプロジェクトをfirebase init
コマンドで初期化します。注意としては、hostingを選択することと、build後に生成されるフォルダはdist(not public)なので、distを指定することです。
上記を実施後、
firebase deploy
コマンドでデプロイします。
問題がなければ、https://<your project>.firebaseapp.com/status/
などにアクセスして確認できます。7. CloudBuildでCI環境構築
CloudBuildでCI環境構築構築し、githubにコードをデプロイすると、CloudBuildがトリガーされ、FirebaseHostingへ自動デプロイの仕組みも作りました。CloudBuild側の設定は、別途サイトを参考ください。
準備としては、Dockerfileを作成し、cloudbuild.yamlで設定を入れることで動作しました。
ちなみに、.gitignore
ファイルに.env
が記載されているので、CloudBuildで連携する場合は、.envもリポジトリに上げるようにする必要があります。Dockerfile# use latest Node 10 FROM node:10 # install Firebase CLI RUN npm install -g firebase-tools ENTRYPOINT ["/usr/local/bin/firebase"]steps: # Install - name: 'gcr.io/cloud-builders/npm' args: ['install'] # Build - name: 'gcr.io/cloud-builders/npm' args: ['run', 'build'] # Docker Image - name: 'gcr.io/cloud-builders/docker' args: ['build', '-t', 'gcr.io/$PROJECT_ID/firebase', '.'] # Deploy - name: 'gcr.io/$PROJECT_ID/firebase' args: ['deploy', '--project', '$PROJECT_ID'] # push built images to Container Registry images: ['gcr.io/$PROJECT_ID/firebase']おわりに
Vue.js / Nuxt.js のおかげで比較的簡単に、ブラウザ上でFirestoreデータを確認し、更新があったらアラートが上がる機能を作成できました。
もう少し応用的なものも作ってみたいと思うのと、環境面では、FirebaseHostingではなく、Google App Engineにもデプロイができるようなので、そちらも試してみたいと思います。
参考コンテンツ
- Udemy 中村祐太「Nuxt JS入門決定版!Vue.jsのフレームワークNuxt JSの基本からFirebaseと連携したSPAの開発まで」
- Nuxt.js プロジェクトを Firebase Hosting にデプロイして公開する方法
- Nuxt.jsを使うときに、SPA・SSR・静的化のどれがいいか迷ったら
- Nuxt.jsのストアをモジュールモードで使用するときのTips
- Nuxt.js + Firebase で vuexfire を使って Cloud Firestore と連携する(データバインド編)
- 細かすぎるけど伝わって欲しいlodash.jsの話
- 投稿日:2019-11-29T13:08:33+09:00
Ant Design Vue の Message で複数行表示させる
AntDesign の MessageAPI
https://www.antdv.com/components/message/
AntDesignを使っていれば
this.$message.info('Hello')
などで利用が可能となっています。素晴らしいですね。
複数行表示したい
ヒーリングっど♥プリキュアは2020年春からの放映なのでそれも伝える必要があります。
なので2行表示にしたかったためとりあえず改行コードを入れてみます。もちろん
<br>
を入れてもそのまま表示されてしまいます。
このままでは来年春から始まる新しいプリキュアの素晴らしさがうまく伝わりません。Stringでは無理
MessageAPIの
info
関数は第一引数にstring | VNode | (h) => VNode
を取ります。
ということでstring
では無理そうなので他のものを試します。VNodeって?
こちら で解説してくれていますが仮想ノードにあたります。
要するにこのあたりには直接仮想ノードを渡せますよーということみたいです。VNodeの作り方
ドキュメント にもあるとおり
createElement
関数で作成するのが一般的なようです。
しかしrender
内ではないので該当の関数がありませんがh
がそれにあたるようです。createElement を h にエイリアスしていることは、 Vue のエコシステムの中でよく見かける慣習です。そして、それは実は JSX には必須です。Vue の Babel プラグインの バージョン 3.4.0 以降では、ES2015 のシンタックスで宣言された JSX を含むメソッドや getter(関数やアロー関数は対象外)に対しては、自動的に const h = this.$createElement が注入されるため、(h) パラメーターは省略できます。それ以前のバージョンでは、もし h がそのスコープ内で利用可能でない場合、アプリケーションはエラーを throw するでしょう。
ということだそうです。
ん?this.$createElement
でもええんか…?ということで
this.$message.info(h => { return h('span', [ h('span', 'ヒーリングっど♥プリキュア'), h('br'), h('span', '2020年春からスタート!') ]) })ヒャッホウ!!
改行だけでなくハートマークを赤くもできますね。面倒くさくない?
JSX でもできるみたいです、使えるなら絶対そっちのがいい。
公式もそう言ってます。次は @karur4n が何かしら書いてくれると思います。
面白い人なので記事もきっと面白いんだろうなあと思います。
ぜひよろしくお願いします。
- 投稿日:2019-11-29T10:45:32+09:00
vuetifyjsのv-selectにてitemsのキーがtextとvalueじゃないときの対処法
はじめに
vuetifyjsのv-selectでは、デフォルトのitemsの仕様は以下となっています
{ text: string | number | object value: string | number | object }itemsにtextとvalueがないときの設定に迷ったので、備忘録を兼ねてメモを残します
対処法
itemsとして、以下のオブジェクトを使用する場合のコード例です
{ [ { categoryName: "カテゴリ1", id: 1 }, { categoryName: "カテゴリ2", id: 2 }, { categoryName: "カテゴリ3", id: 3 }, { categoryName: "カテゴリ4", id: 4 } ] }textにcategoryName、valueにidを使用する場合、v-selectのitem-valueとitems-textにitemsの対応するキーを指定します。ここでは以下のようにします
- text : categoryName
- value : id
<v-select v-model="id" label="*カテゴリ名" :items="categories" item-text="categoryName" item-value="id" required> </v-select>参考
- 投稿日:2019-11-29T09:54:07+09:00
Vue.jsでHTMLのバリデーションが効かなくて詰まった時の話
inputタグに使用できる入力必須の機能
http://www.htmq.com/html5/input_required.shtml
入力してなかったら弾かれるという基礎的なバリデーションの機能をつけようとして、よく使用するhtmlの機能のrequired="true"で処理しようとした。が、バリデーションが効かず入力してなくても次のページへ遷移してしまう。。。
HTML、CSSに関しては個人アプリやスクールで利用していたため、requiredが効かない原因がVue.jsの何らかの機能のせいなのだろうということは何となくわかったのですが、知識がなくてどこでバリデーションが無効になっているのかがわからず...
原因
submitボタンでの処理が原因でした。
//template部分 @on-click="$emit('next'); submit()"この一行。
//js部分 methods: { submit() { this.$emit("update", { eventId: this.$route.params.id, Name: this.Name, mailAddress: this.mailAddress, }); } }クリックした時に関数のsubmitが発火。$emit('next')で次に遷移している。
この$emit('next')で、バリデーションに引っかかってようがいまいが、データを持って次に遷移してしまっている。
なので、バリデーションをかけるのはsubmitの関数の中。methods: { submit() { if(this.eventId && this.eventId && this.eventId){ this.$emit("update", { eventId: this.$route.params.id, Name: this.Name, mailAddress: this.mailAddress, }); this.$emit('next'); } } }こうすることで、入力していないとボタンが押せなくなりました。
- 投稿日:2019-11-29T09:53:24+09:00
Vue.jsを使った初めてのフロント実装
Vue.jsとは?
https://lab.aratana.jp/entry/2017/12/09/000000
↑参考資料jQueryは少し触ったことがありますが、動きをつけたい部分にちょこっと使用していただけのため、そこまでJavaScriptに詳しいわけでもなく....。
本当に漠然とした理解なのですが、動きをつけたい部分をそれぞれコンポーネントとして分けて記載し、それをhtmlで記載した部分の骨組みの中で呼び出して使用するという感じでしょうか。
今回はすべてVue.jsでの実装になるので混乱していますが、パーツ利用だともう少し理解しやすいのかなーと思ったり。
知識的にはほぼない状態のスタートなのではやく慣れていきたいです。よく出てくるメソッドのメモ
- v-bind 属性を反映する
下記(HTML側)は、この要素のclass属性を、VueインスタンスのisActiveプロパティによって更新して保存するという処理になっている。
//例) HTML側。この場合、isActiveが真偽値となっており、trueだとactiveクラスが生成される。 <div class="app"> <div v-bind:class="{ active: isActive }"></div> </div> //例) js側 new Vue({ el: 'app', data: { isActive: true } });
- v-for(htmlとjsがある) 配列の繰り返しを扱う
//例) HTML側。listsという配列の入ったメソッドを、listというメソッドに代入して繰り返し処理 <div id="app2"> <li v-for="list in lists" :key="list.id"> {{ list.name }} <li> </div> //例) js側 var vueApp= new Vue({ el: '#app2', data: { lists: [ { name: 'Vue.js' }, { name: 'Angular.js' }, { name: 'React' } ] } }); vueApp.lists.push({ name: 'Nuxt.js'}) //この記載でvueAppの中の配列listsにname:'Nuxt.js'を追加できる。
- v-model(htmlとjsに) 入力を反映する
//例) HTML側 <div id="app4"> <p>{{ message }}</p> <input v-model="message"> </div> //例) js側 new Vue({ el: '#app4', data: { message: 'Hello Vue!' } })local-strageとは??
Cookieとかsessionみたいなもの。
検索機能つけるときに。
https://qiita.com/RunEagler/items/4496161d3b709659a897
- 投稿日:2019-11-29T00:26:49+09:00
Rails+Vueのユーザー登録実装でムカついたのでメモ!!!
やったこと
deviseなどのgemを使わずユーザー登録機能をrailsとvueで実装
ハマったこと
ユーザー登録フォームに入力してPOSTした時、:userの中にpasswordとpassword_confirmationの値が入らず(ネストせず)かなり苦戦。
具体的にはこんな感じ。
理想形
コンソールParameters: {"user"=>{"name"=>"hogehoge", "email"=>"fugafuga@test.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}NG形
Parameters: {"name"=>"", "email"=>"", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "user"=>{"name"=>"", "email"=>""}}passwordとpassword_confirmationだけuserの中に入ってこない!!
まあNG形の場合でもコントローラーのストロングパラメーターで
users_controller.rbprivate def user_params params.permit(:name, :email, :password, :password_confirmation) endとすれば大丈夫なんだけど、どうしてもカッコよくこう書きたい!
users_controller.rbprivate def user_params params.fetch(:user, {}).permit(:name, :email, :password, :password_confirmation) endvueで書くとhas_secure_password効かないのか〜?とか、formの書き方が悪いのか〜?とか、とにかく色々と原因を考えまくって時間を浪費したこと浪費したこと。
こういう時プログラミング嫌いになる
そんな話はさておき、本題を続けます。
Vueの書き方
先にNGだった書き方から。(js部分のみ。かなり省略してます)
NGの書き方
NewForm.vueimport axios from 'axios'; export default { data: () => ({ user: { name: '', email: '', password: '', password_confirmation: '', }, }), methods: { createUser: function() { axios .post('/api/v1/users', this.user) .then(response => { var res = response.data; this.$router.push({ name: 'home' }) }) .catch(error => { if(error.response.data && error.response.data.errors) { this.errors = error.response.data.errors; } }) },これだとNG形になります。
理想形のパラメーターが返ってくる書き方
NewForm.vueimport axios from 'axios'; export default { data: () => ({ name: '', email: '', password: '', password_confirmation: '', }), methods: { createUser: function() { axios .post('/api/v1/users', { user: { name: this.name, email: this.email, password: this.password, password_confirmation: this.password_confirmation }}) // 以下、同じなので省略POSTする時に、こうやって明示的に書けばOKだった!
どうしても納得いかないのが、NGの時もnameとemailはちゃんとネストされてたってこと。
passwordとpassword_confirmationは、テーブルのカラムに存在しないから取れなかったと思うんだけど、has_secure_passwordを書いててもダメなもんかい初歩的な実装に、ものすごい時間がかかってしまったというお話でした。
ではまた!