- 投稿日:2020-01-21T23:12:30+09:00
Vue.js の基本的な機能を使ったサンプルを書く
概要
- Mustache 構文、条件付きレンダリング、メソッド、算出プロパティ、フォーム入力バインディング、リストレンダリング、コンポーネントの機能を使ったサンプルコードを示す
- 環境: Vue.js 2.6.11
Mustache 構文で Hello World
- 画面に Hello, world! と表示するサンプルコード
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello, world!</title> </head> <body> <div id="app"> <!-- Mustache 構文で message プロパティを表示 --> <p>{{ message }}</p> </div> <!-- デバッグに便利な Vue.js の開発バージョンを使う --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> // Vue インスタンスを生成 new Vue({ // Vue インスタンスが管理する DOM 要素 el: '#app', // プロパティ data: { message: 'Hello, world!' } }) </script> </body> </html>データバインディングのもっとも基本的な形は、”Mustache” 構文(二重中括弧)を利用したテキスト展開です:
条件付きレンダリング、メソッド、算出プロパティ
- 「カウントアップ」ボタンを押すとカウント数が1増える
- カウント数を表示する
- 3の倍数のときはカウント数ではなく「あほー」と表示する
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Counter</title> </head> <body> <div id="myCounterDiv"> <!-- DOM イベントをトリガーに設定 --> <button v-on:click="myCountUp">カウントアップ</button> <!-- データバインディング --> <!-- v-bind で title 属性にセット --> <!-- Mustache 構文で要素内に表示 --> <p v-bind:title="myMessage">カウンター: {{ myMessage }}</p> <!-- 条件付き描画 (Conditional Rendering) --> <p v-if="myCounter % 6 == 0">6の倍数ですね</p> <p v-else-if="myCounter % 3 == 0">3の倍数ですね</p> <p v-else-if="myCounter % 2 == 0">2の倍数ですね</p> <p v-else>2の倍数でも3の倍数でもないですね</p> </div> <!-- サイズと速度が最適化された Vue.js の本番バージョンを使う --> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> // Vue インスタンスを生成 var vm = new Vue({ // Vue インスタンスが管理する DOM 要素 el: '#myCounterDiv', // プロパティ data: { myCounter: 0 }, // created フック // インスタンスが作成された後に同期的に呼ばれる created: function() { this.myCounter = 1 }, // メソッド methods: { myCountUp: function (event) { this.myCounter++ } }, // 算出プロパティ (computed properties) computed: { myMessage: function () { if (this.myCounter % 3 == 0) { return "あほー" // 3の倍数のときに返す値 } else { return this.myCounter } } } }) </script> </body> </html>v-on ディレクティブを使うことで、DOM イベントの購読、イベント発火時の JavaScript の実行が可能になります。
v-bind
1つ以上の属性またはコンポーネントのプロパティと式を動的に束縛します。
v-if ディレクティブは、ブロックを条件に応じて描画したい場合に使用されます。ブロックは、ディレクティブの式が真を返す場合のみ描画されます。
Vue インスタンスが作成されると、自身の data オブジェクトの全てのプロパティをリアクティブシステムに追加します。これらのプロパティの値を変更すると、ビューが”反応”し、新しい値に一致するように更新します。
各 Vue インスタンスは、生成時に一連の初期化を行います。例えば、データの監視のセットアップやテンプレートのコンパイル、DOM へのインスタンスのマウント、データが変化したときの DOM の更新などがあります。その初期化の過程で、特定の段階でユーザー自身のコードを追加する、いくつかの ライフサイクルフック(lifecycle hooks) と呼ばれる関数を実行します。
例えば、created フックはインスタンスが生成された後にコードを実行したいときに使われます。
算出プロパティの代わりに、同じような関数をメソッドとして定義することも可能です。最終的には、2つのアプローチは完全に同じ結果になります。しかしながら、算出プロパティはリアクティブな依存関係にもとづきキャッシュされるという違いがあります。
フォーム入力バインディング、リストレンダリング
- フォームに入力したテキストをリストに追加
- リストを表示
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>List</title> </head> <body> <div id="myListDiv"> <!-- フォーム入力バインディング --> <input v-model="myItem" placeholder="アイテム名"> <!-- DOM イベントをトリガーに設定 --> <button v-on:click="myAddItem">{{ myItem }} を追加</button> <!-- 配列を表示 --> <ul> <li v-for="item in myItemList"> {{ item.name }} </li> </ul> </div> <!-- Vue.js のバージョン 2.6.11 を使う --> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script> <script> var vm = new Vue({ // Vue インスタンスが管理する DOM 要素 el: '#myListDiv', // データ data: { myItem: '', myItemList: [ { name: 'やくそう' }, { name: 'どくけーしー' } ] }, // メソッド methods: { myAddItem: function() { if (this.myItem !== '') { this.myItemList.push({name: this.myItem}) this.myItem = '' } } }, }) </script> </body> </html>form の input 要素 や textarea 要素、 select 要素に双方向 (two-way) データバインディングを作成するには、v-model ディレクティブを使用することができます。
配列に基づいて、アイテムのリストを描画するために、v-for ディレクティブを使用することができます。v-for ディレクティブは item in items の形式で特別な構文を要求し、items はソースデータの配列で、item は配列要素がその上で反復されているエイリアスです:
コンポーネント
- それぞれ別のカウント値を持ったカウンター
- ボタンを押すとカウント数が1増える
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Counters by Components</title> </head> <body> <div id="components-demo"> <!-- それぞれが別のインスタンスになるため、それぞれ別の count プロパティを保持する --> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter> </div> <!-- デバッグに便利な Vue.js の開発バージョンを使う --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> // button-counter という新しいコンポーネントを定義 // コンポーネントは再利用可能な Vue インスタンス Vue.component('button-counter', { // コンポーネントの data オプションは関数でなければならない data: function () { return { count: 0 } }, // Vue インスタンスに対して使用するテンプレート文字列 // クリックするとカウントアップ count++ template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' }) new Vue({ // Vue インスタンスが管理する DOM 要素 el: '#components-demo', }) </script> </body> </html>コンポーネントは再利用可能な Vue インスタンスなので、data、computed、watch、methods、ライフサイクルフックなどの new Vue と同じオプションを受け入れます。唯一の例外は el のようなルート固有のオプションです。
コンポーネントのプロパティ
- 表示情報のデータをコンポーネントに渡す
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Blog Posts by Components</title> </head> <body> <div id="components-demo"> <!-- 定義した blog-post コンポーネントを表示 --> <!-- v-bind:key で仮想 DOM 処理ヒント用にユニークなキーである myPost.id を指定 --> <!-- blog-post の post 属性に値 myPost を渡す --> <blog-post v-for="myPost in myPostList" v-bind:key="myPost.id" v-bind:post="myPost" ></blog-post> </div> <!-- デバッグに便利な Vue.js の開発バージョンを使う --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> // blog-post という新しいコンポーネントを定義 // コンポーネントは再利用可能な Vue インスタンス Vue.component('blog-post', { // データを受け取るためのプロパティ props: ['post'], // HTML 描画用テンプレート // Mustache 構文でタイトルを出力 // v-html で HTML をそのまま出力 template: ` <div class="blog-post"> <h3>{{ post.title }}</h3> <div v-html="post.content"></div> </div> ` }) new Vue({ el: '#components-demo', data: { myPostList: [ { id: 1, title: 'たいとる1', content: '<p>なかみ1</p>' }, { id: 2, title: 'たいとる2', content: '<p>なかみ2</p>' }, { id: 3, title: '>_<;', content: '<p>こうですか!? わかりません><</p>' } ] } }) </script> </body> </html>プロパティはコンポーネントに登録できるカスタム属性です。値がプロパティ属性に渡されると、そのコンポーネントインスタンスのプロパティになります。
参考資料
- 投稿日:2020-01-21T19:08:05+09:00
Blocklyに基づきビジュアルプログラミングの入門級の実例(三、VUE環境でBlocklyのコードを非同期的に処理するサンプル)
本章では、VUEのシンプルなサンプルで、BlocklyのBlockから生成コードを非同期的に実行する方法を説明しております。
Blocklyのコードをステップ毎に実行する時は、Interpreterライブラリをよく使っているんですけど、Interpreterライブラリを使いたくない場合は、JavaScriptのeval関数でBlocklyのコードを実行することも可能です。
evalは一般的に同期でソースコードを実行されているんで、非同期的に実行したい場合はどうしたらよいかについてのことを、以下のサンプルで説明して見ましょう。事前準備
Blockly
公式サイト:https://developers.google.com/blockly/
Web版の資材:https://developers.google.com/blockly/guides/get-started/web#get_the_codeVUE
https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.22/vue.min.js
サンプル
testblock.js
このファイルで、画面表示用のブロックを作成します
BlocklyのDevelopToolから生成することができます。Blockly.Blocks['block_asyncplay'] = { init: function() { this.appendStatementInput("MUSIC") .setCheck(null) .setAlign(Blockly.ALIGN_RIGHT) .appendField("play"); this.setInputsInline(true); this.setPreviousStatement(true, null); this.setNextStatement(true, null); this.setColour(120); this.setTooltip(""); this.setHelpUrl(""); } }; Blockly.JavaScript['block_asyncplay'] = function(block) { var statements_music = Blockly.JavaScript.statementToCode(block, 'MUSIC'); var code = "async function asyncplay() {\n" + statements_music + "\n}\n"; return code; }; Blockly.Blocks['block_play'] = { init: function() { this.appendDummyInput() .setAlign(Blockly.ALIGN_RIGHT) .appendField("play"); this.setInputsInline(true); this.setPreviousStatement(true, "String"); this.setNextStatement(true, "String"); this.setColour(120); this.setTooltip(""); this.setHelpUrl(""); } }; Blockly.JavaScript['block_play'] = function(block) { var code = "await play(1000);\n"; return code; };test01.html
画面にブロックを表示するためのHTMLファイルです。
<script src='https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.22/vue.min.js'></script><!-- 2019-01-25 https://cdnjs.com/libraries/vue --> <script src="../blockly-master/blockly_compressed.js"></script> <script src="../blockly-master/blocks_compressed.js"></script> <script src="../blockly-master/javascript_compressed.js"></script> <script src="../blockly-master/msg/js/en.js"></script> <script src="./testblock.js"></script> <body> <div id="vue_example"></div> <script> var vue_example = new Vue({ el: '#vue_example', template: `<div> <div width="600px" height="50px"> <button v-on:click="test()">test</button> </div> <div width="600px" height="600px"> <div id="blocklyDiv" style="height: 100%; width: 100%;"></div> <xml id="toolbox" ref="toolbox" style="display: none"> <block type="block_asyncplay"></block> <block type="block_play"></block> </xml> <xml id="workbox" ref="workbox"> <block type="block_asyncplay" id="id_block_asyncplay" x="10" y="30"> <statement name="MUSIC"> <block type="block_play" id="id_block_play_01"> <next> <block type="block_play" id="id_block_play_02"> <next> <block type="block_play" id="id_block_play_03"></block> </next> </block> </next> </block> </statement> </block> </xml> </div> </div>`, data: { message: 'Hello Vue.js!', }, mounted() { var workbox = this.$refs["workbox"]; var options = { toolbox: toolbox, collapse: true, comments: true, disable: true, maxBlocks: Infinity, trashcan: true, horizontalLayout: false, toolboxPosition: 'start', css: true, rtl: false, scrollbars: true, sounds: true, oneBasedIndex: true, grid: { spacing: 20, length: 1, colour: '#888', snap: true } } /* Inject your workspace */ this.workspace = Blockly.inject('blocklyDiv', options) //Workspaceに書かれたBlocksを表示 Blockly.Xml.domToWorkspace(workbox, this.workspace); }, methods: { test: function () { var dom = Blockly.Xml.workspaceToDom(this.workspace); console.log( Blockly.Xml.domToText(dom)); var code = Blockly.JavaScript.workspaceToCode(this.workspace); console.log( code ); eval(code); asyncplay(); async function play(waittime) { return new Promise((resolve, reject) => { console.log("play waittime:" + waittime); setTimeout(resolve, waittime, 'time signature'); }); } }, }, }) </script> </body> </html>実行結果
- 投稿日:2020-01-21T19:05:55+09:00
【Nuxt.js】 middleware is 何?
ミドルウェアとは
- ミドルウェアを使用すると、ページがレンダリングされる前(SSR処理などが行われる前)に設定された関数を実行することができる
- 認証許可が必要なページやログイン後のリダイレクトパスを保持するために使用される。
- 関数は
middleware/
ディレクトリに入れる
middleware/auth.js
はauth
ミドルウェアになる- ミドルウェアは第一引数に
context
を取る
context
の内容についてはこちらを参照- ユニバーサルモードの場合は、サーバーサイドで一度だけ呼び出される。 (Nuxtアプリケーションへの最初のリクエスト時、またはページの再読み込み時)クライアントサイドでは、他のルートへ移動した時に呼び出される。
- SPAモードの場合、クライアントサイドでの最初のリクエスト時と他のルートへ移動した時に呼び出される。
middlewareを使用した処理の例
storeのアカウント情報が無い場合、ログイン画面にリダイレクトされる処理を書いてみる。
middleware/redirect.jsexport default function ({ redirect, store }) { const user = store.state.user if(!user) { redirect('/login') } }これを
nuxt.config.js
で読み込む。nuxt.config.jsexport default { router { middleware: 'redirect' } }このようにすると全てのルート変更時に
redirect.js
が読み込まれるようになる。また、特定のページ(またはレイアウト)にのみ特定の関数を設定することもできる。
index.vue<script> import { fetchUid } from '@/middleware/uid.js' export default { middleware: [ 'auth', fetchUid ] } </script>1ファイルに1つの処理の場合は、'auth'で関数を実行することができる。
一方、1つのファイルに複数の関数がある場合は、ファイルをimportすることで特定の関数を実行することができる。middlewareの実行順序について
middleware
自体の実行順序は、このようになっている。
plugins
→middleware
→fetch
→asyncData
middleware
の関数の呼び出し方の違いによる実行順序はこのようになる。
nuxt.config.js
→layout
→page
Login画面では実行したくない問題
アカウント情報を確認してリダイレクトさせる処理を
middleware
内に書いた場合、Login画面ではまだアカウント情報がないので、関数を実行したくない場合がある。
その場合は、middleware
内のcontext
にroute
があるので、そこから関数を実行したくないページを弾いて処理を実行させるようにする。middleware/auth.jsexport default function ({ redirect, store, route }) { const user = store.state.user if(!user && route.path !== '/login') { redirect('/login') } }
- 投稿日:2020-01-21T18:59:25+09:00
Vuetify2.xでv-data-tableで行クリックと操作列追加
Vuetify2.xで変更されたV-Data-Tableをカスタマイズするときのメモ
概要
Vuetify1.5から2.2にあげたらV-Data-Tableの書き方が変わっていた。
1.5→2.0にあげた例を参考にさせていただいた。
V-Data-Tableに関しては、リンク先にあるようにカラム指向になったようなイメージでtemplateの書き方がかわったようだった。
最初はV-Data-Iteratorでまわして自力でTableを構築する必要があるのかな、とおもったが公式ドキュメントに例があったので、参照しつつ解決したときのメモ。行クリック
ドキュメントのAPI欄にさりげなく click:row とあったので試した。
単にイベントに関数を割り当てるだけでよかった。
抜粋:
vue<template lang="pug"> v-data-table( :headers="headers" :items="db" hide-default-footer @click:row="clickRow" ) </template> <script> export default { name: "List", data: () => ({ headers: [ {text:'ID', value:'id'}, {text:'なかまたち', value:'name'} ], db: [ {id:1, name:'ブタさん'}, {id:2, name:'ウサギさん'}, {id:3, name:'キツネさん'}, {id:4, name:'ゾウさん'} ] }), methods: { clickRow(row) { console.log(row.name) // 1 => ブタさん... } } </script>1行ごとに操作アイコンなどデータ以外の列を表示
公式ドキュメントの例どおりに
- ヘッダー列定義に操作用の列を追加
- templateで操作列を定義
ヘッダ列への追加
vueスクリプト抜粋data: () => ({ headers: [ {text:'ID', value:'id'}, {text:'なかまたち', value:'name'}, {text:'操作', value:'action'} ], ~~~表示用template
vueのtemplate<template lang="pug"> v-data-table( :headers="headers" :items="db" hide-default-footer @click:row="clickRow" ) // 各行・列の描画はよろしくやってくれるので操作列だけ記述すればよい template(v-slot:item.action="{item}") v-btn(@click="showName(item.name)") v-icon fa-home </template> <script> (略) showName(str) { console.log(str) } (略) </script>まとめ
どうかけばいいのか調べるのに時間がかかったけど、結局公式ドキュメントをみて試してなんとかなった。
簡潔にかけるので1.5よりも楽じゃないだろうか。
特定の列だけ表示方法をかえるというのも例のとおりでなんとかなるのだろう。上記例、それぞれなら問題ないが、行クリックと、操作ボタンを両方設定したいた場合、どちらも同時に実行される部分は未解決(ぐぬぬ)
- 投稿日:2020-01-21T18:28:20+09:00
【Nuxt.js】pagination実践編:$router.pushで簡単実装!
前置き
ページ数に応じて
urlと表示が変わるpaginationです?
前回やった導入編と全く別物です!
こっちの方が簡単なので別パターンとして紹介?記事タイトルが紛らわしいので、
まとめて名称変えるかもしれません。
こちらの続きはまた別記事にて…!
https://note.com/aliz/n/n8bb439a426a8firebaseを月曜日に公開予定でしたが、
Cloud Firestoreがバグり。。。
それが落ち着いてからにします☁️
今後は火・木に投稿していく予定です!構成
・pagination部分をコンポーネント化
・使用するページからpropsでdataを渡す?
・ページ数をurlに表示させる?($router.push)
・全7ページで、ページ数に応じて表示を変更Step1: コンポーネントでpropsを用意
【構成】
使うコンテンツによって
最大ページ数などが変わるためpropsを使用
・query: ページネーションを使うコンテンツ
・length: ページの長さ
・now: 今いるページPagination.vue<script> export default { props: { query: { type: String, required: true, }, length: { type: Number, required: true, }, now: { type: Number, required: true, }, }, } </script>Step2: コンポーネントで戻る・進むボタンを追加
【式】三項演算を使用
式1 ? 式2 : 式3
式1がtrueなら式2、falseなら式3Pagination.vue<template> <div> <button class="btn btn-prev" @click="$router.push(`?${query}=${now - 1 || 1}`)" > 戻る </button> <button class="btn btn-next" @click="$router.push(`?${query}=${now + 1 <= length ? now + 1 : length}`)" > 進む </button> </div> </template> <script> export default { props: { query: { type: String, required: true, }, length: { type: Number, required: true, }, now: { type: Number, required: true, }, }, } </script>【解説】
◾️戻る
・|| または該当コンテンツページ内queryの
今いるページnowから1戻る、または1にする◾️進む
該当コンテンツページ内の
今いるページに1を足して
・全体ページ数と同じ
・またはそれより小さい場合
今いるページから1進む
そうでなければ全体ページ数にするつまり全体ページを7で、
現在いるページが7なら
7 + 1 <= 7
falseになるので7のまま
それ以上進むことはないですね?あれ??
【戻る】すごくシンプルに見えるのに…Pagination.vue@click="$router.push(`?${query}=${now - 1 || 1}`)"【進む】は何か長い。
Pagination.vue@click="$router.push(`?${query}=${now + 1 <= length ? now + 1 : length}`)"これではダメなの????
Pagination.vue@click="$router.push(`?${query}=${now + 1 || length}`)"最大ページ数を越えてどんどん進みます笑
lengthの値はマイナスにはできません。
そのため制限をかけなくても
勝手に1で止まってくれるのですが…!
プラスは制限をかけないと止まりません?♀️?Step3: コンポーネントでページ数を表示
【構成】
ページ数の表示部分を作りましょう!
・5ページまではページ数分のみ表示
・6ページ以上は…(三点リーダー)で中間を省略【CSS】
毎度のことですが省きます。
・…はcssでdotクラスでborderを使用
・現在ページがをクラスバインディングで
background-color, colorを変更?【if, if, if…】
ifで沢山分岐しています笑
どこで並列になってるか分かりにくいですね?
コンパクトにして全体構造を把握しましょう。【Pagination.vue】
主にインラインのコメントで解説!
コードでも並列部分を絵文字で区別しています。
?と?が並列で使われている部分です。
それ以外の絵文字は if の目印です!Pagination.vue<template> <div // ページ数が1より大きい、2ページ以上の時のみページネーションを表示 v-if="length > 1" class="list-item list-item-nav" > <button class="btn btn-prev" @click="$router.push(`?${query}=${now - 1 || 1}`)" > 戻る </button> <ul class="list"> // 1ページ目はどんな時でも固定表示のためif不要 <li // クラスバインディング、{ class名: 式 }でtrueの時にクラスがつく :class="{ now: now === 1 }" class="item item-link" // 1ページを押すとurlが~/1になる @click="$router.push(`?${query}=1`)" > <span class="text"> 1 </span> </li> // ?ここから分岐、最大ページ数が2より大きい3〜 <template v-if="length > 2"> // ?3以上5以下(=最大ページ数3,4,5の時) 5ページまでの場合は、最大ページ数に応じて該当ページ数を表示 <template v-if="length <= 5"> <li :class="{ now: now === 2 }" class="item item-link" @click="$router.push(`?${query}=2`)" > <span class="text"> 2 </span> </li> // ?最大ページ数が3, 4, 5かつ3より大きい4, 5の時 <template v-if="length > 3"> <li :class="{ now: now === 3 }" class="item item-link" @click="$router.push(`?${query}=3`)" > <span class="text"> 3 </span> </li> // ?最大ページ数が3, 4, 5かつ3より大きい4, 5かつ4より大きい5の時 <template v-if="length > 4"> <li :class="{ now: now === 4 }" class="item item-link" @click="$router.push(`?${query}=4`)" > <span class="text"> 4 </span> </li> </template> </template> </template> // ?でなければ(=最大ページが5より大きい6〜) <template v-else> // ?最大ページ6〜かつ現在いるページが4より少ない(=1, 2, 3の時) <template v-if="now < 4"> <li :class="{ now: now === 2 }" class="item item-link" @click="$router.push(`?${query}=2`)" > <span class="text"> 2 </span> </li> <li :class="{ now: now === 3 }" class="item item-link" @click="$router.push(`?${query}=3`)" > <span class="text"> 3 </span> </li> <li // ?現在いるページが4より少ないかつ、3ページ目にいる時 v-if="now === 3" class="item item-link" @click="$router.push(`?${query}=4`)" > <span class="text"> 4 </span> </li> <li class="item item-dots"> <div class="dot" /> <div class="dot" /> <div class="dot" /> </li> </template> // ?最大ページ6〜かつ現在いるページが1, 2, 3でなく4で〜 現在いるページに2を出しても最大ページ数と同じか少なければ (4ページ目にいるなら4 + 2、最大ページ7の方が大きいためfalse) (6ページ目にいるなら6 + 2、最大ページ7より大きいためtrue) <template v-else-if="length <= now + 2"> <li class="item item-dots"> <div class="dot" /> <div class="dot" /> <div class="dot" /> </li> // ?最大ページ数から2を引いた数字が現在いるページだったら 最大ページ数から3を引いたページ数を表示させる (5ページ目にいるなら7-2 =5でtrue、7-3 =4が表示される) <li v-if="now === length - 2" class="item item-link" @click="$router.push(`?${query}=${length - 3}`)" > <span class="text"> {{ length - 3 }} </span> </li> <li :class="{ now: now === length - 2 }" class="item item-link" @click="$router.push(`?${query}=${length - 2}`)" > <span class="text"> {{ length - 2 }} </span> </li> <li :class="{ now: now === length - 1 }" class="item item-link" @click="$router.push(`?${query}=${length - 1}`)" > <span class="text"> {{ length - 1 }} </span> </li> </template> // ?最大ページ6〜かつ、今までのパターンに該当しない (上の?のfalse、現在4ページの場合) <template v-else> <li class="item item-dots"> <div class="dot" /> <div class="dot" /> <div class="dot" /> </li> <li class="item item-link" @click="$router.push(`?${query}=${now - 1}`)" > <span class="text"> {{ now - 1 }} </span> </li> <li class="item item-link now"> <span class="text"> {{ now }} </span> </li> <li class="item item-link" @click="$router.push(`?${query}=${now + 1}`)" > <span class="text"> {{ now + 1 }} </span> </li> <li class="item item-dots"> <div class="dot" /> <div class="dot" /> <div class="dot" /> </li> </template> </template> </template> <li :class="{ now: now === length }" class="item item-link" @click="$router.push(`?${query}=${length}`)" > <span class="text"> {{ length }} </span> </li> </ul> <button class="btn btn-next" @click="$router.push(`?${query}=${now + 1 <= length ? now + 1 : length}`)" > 進む </button> </div> </template> <script> export default { props: { query: { type: String, required: true, }, length: { type: Number, required: true, }, now: { type: Number, required: true, }, }, } </script>これで完成です?
【最大ページ2は?】
if は最大ページ3以上で分岐。
2はどうなっているかというと…
ul 内の構造を黄色い枠で分けています?
・1固定表示
・3ページ以上で分岐
・最大ページ固定表示Step4: コンテンツページでpropsにdataを渡す
【sample.vue】
Pagination.vue<template> <div class="page"> <Pagination query="members" :length="7" :now="Number($route.query.members) || 1" class="nav" /> </div> </template> <script> import Pagination from '~/components/Pagination.vue' export default { components: { Pagination, }, data() { return { members: [ { name: aLiz }, ], } }, } </script> <style lang="scss" scoped> </style>このアカウントでは
Nuxt.js、Vue.jsを誰でも分かるよう、
超簡単に解説しています??
これからも発信していくので、
ぜひフォローしてください♪
https://twitter.com/aLizlab
- 投稿日:2020-01-21T17:27:26+09:00
Vue.jsについて基礎的なことを少しまとめました
vue.jsとは
- データと表示を繋げる仕組みです。
- MVVM(Model-View-ViewModel)という考え方を元に作られている
MVVM(Model-View-ViewModel)とは
- Model・・・Vue内に用意したデータ
- View・・・HTMLで表示している要素
- ViewModel・・・それらをどのように繋ぐか
MVVMの仕組みを考える際
- データは何か
- web上で変化する部分は何か。そのために必要なデータを考える。
- 表示する要素は何か
- そのデータを、HTMLのどこに、どのように表示するかを考える
- どのように繋ぐか
- HTMLのどこを操作された時にデータがどのように変化するかを考える
という流れで考える
データを作って、表示する要素を用意して、繋ぎ方を決める!
Vue.jsの主な機能
機能 書式 データをそのまま表示する {{データ}} 要素の属性をデータで指定する v-bind 入力フォームとデータを繋げる v-model イベントと繋ぐ v-on 条件によって表示する v-if 繰り返し表示する v-for データを使って別の計算をする computed データの変化を監視する watch 表示/非表示にアニメーションする transition 部品にまとめる component などなど・・・
参考図書
- 投稿日:2020-01-21T17:00:49+09:00
vue.jsのインスタンスを理解したいと思ったので
vue.jsをふわっと触って動くものを作るなど学習していますが、vue.jsのインスタンスという概念についてあまり理解出来ていないようだったので調べてみました。
まず
- vue.jsでSPAを作るには、まず、Vueインスタンスを作るところから始める
- SPAを裏で動かしている実態
vue.jsnew Vue ({インスタンスの中身})または
vue.jsvar 変数名 = new Vue({ Vue インスタンスの中身})という構文で書きます。
インスタンスのなかで、下記のようにずらずらっとオプションというものがあります
この中で、一番上にある「data」というオプションは、「どんなデータがあるか」を指しています。
書き方は、
vue.jsnew Vue ({ el:"#ID名", data:{ プロパティ:値 // ←データ部分 プロパティ:値 // ←データ部分 } })という感じで書きます。
dataの「どんなデータがあるか」 の他に、
- どのHTML要素と繋がるか (el)
- どんな処理を行うのか (methods)
- どのデータを使って別の計算をするのか (computed)
- どのデータを監視するのか (watch)
などの情報を、Vueインスタンス内に書いていきます。
参考図書
- 投稿日:2020-01-21T13:12:30+09:00
既存のRailsアプリにVue.jsを導入する
環境
ruby 2.6.0
Rails 5.2.4.
Vue 2.6.11gem 'webpacker' のインストール
Gemfilegem 'webpacker', github: 'rails/webpacker'bundleyarn のインストール
$ brew install yarn $ yarn -vwebpacker のインストール
$ bin/rails webpacker:install $ bin/webpackの順に入力。
webpack 関連のファイルが作成・更新される。Vue.js のインストール
$ rails webpacker:install:vueこのコマンドを実行することで、
app/javascript/packs
配下にファイルが作成される。これでvueの導入は完了。
動作確認
viewの操作をしてみる
app/javascript/packs/hello_vue.jsimport Vue from 'vue/dist/vue.esm' #htmlを操作する場合は、 vue/dist/vue.esm をimport const app = new Vue({ el: '#hello', data: { message: "Test text" } })任意のview<div id="hello"> {{ message }} </div> <%= javascript_pack_tag 'hello_vue' %>Test textと表示されればOK。
<%= javascript_pack_tag 'hello_vue' %>でjsファイルの読み込みを行う。
- 投稿日:2020-01-21T12:08:59+09:00
花粉症LINE BotからのデータをWEBカレンダーに表示する(花粉カレンダー作成④)
概要
耳鼻咽喉科の開業医をしています。
花粉症の患者さんに使ってもらえるような花粉飛散情報が分かるカレンダーアプリを作りたいと思っています。
これまでカレンダーを表示して予定を入れることと、ユーザー認証の実装、LINEのデータをFirebaseに貯めるところまで行ってきました。
Vue.js×FullCallendarでWEBカレンダー作成(花粉カレンダー作成①)
Auth0で簡単にユーザー認証を実装(花粉カレンダー作成②)
花粉症LINE Botのデータをnode.jsを使ってFirebaseに出し入れする(花粉カレンダー作成③)今回はLINEBotのデータが記録されているFirebaseのdatabaseのデータをカレンダーに表示することに挑戦しました。
LINEBotの記事はこちら
花粉症の重症度を判定し自分に合う市販薬を教えてくれるLINE Botの作成完成動画
作成
1.FirebaseのRealtime Databaseの確認
LINEのデータはFirebaseのRealtime Databaseに記録されています。
データは以下のように収納されています。今回は以下の情報を取得して重症度や薬剤名、緯度経度をリアルタイムでカレンダーに記入していきたいと思います。
・postback.data(花粉症の重症度や使用している薬剤の情報)
・postback.params.datatime(重症度判定を行った日や薬剤使用開始した日の情報)
・sorce.userID(LINEのユーザーID)
個別の花粉飛散情報を表示するため
・message.latitude(ユーザー位置情報 緯度)
・message.latitude(ユーザー位置情報 経度)データは.(ドット)で深堀していくことができるようです。
2.実装
以前作成したCalendar.vueに追記していきます。
Vue.js×FullCallendarでWEBカレンダー作成(花粉カレンダー作成①)methods: { }の中に以下を追記します。
緯度や経度は本当はデータが取得できるだけでいいのですが、今回は本日の日付で表示してみました。childAdded(snap) { const message = snap.val(); const mes = message.events[0]; if (mes.type == "postback") { console.log(mes.postback.data); console.log(mes.postback.params.datetime); console.log(mes.source.userId); this.calendarEvents.push({ title: mes.postback.data,//重症度や薬剤 start: mes.postback.params.datetime, end: mes.postback.params.datetime }); } if (mes.type == "message") { if(mes.message.type=="location"){ console.log(mes.message.latitude); console.log(mes.message.longitude); userlat = mes.message.latitude;// 緯度 userlong = mes.message.longitude;//経度 }; this.calendarEvents.push({ // title: mes.message.text, title: `緯度${userlat}`, start: "2020-01-19T09:00:00", end: "2020-01-19T10:30:00" }, { title:`緯度${userlong}`, start: "2020-01-19T09:00:00", end: "2020-01-19T10:30:00" } ); } },async created() { }の中に以下を追記して完成です。
const ref_message = firebase.database().ref("protoout/studio/messageList"); //新しいメッセージ2件だけ表示する ref_message.limitToLast(2).on("child_added", this.childAdded);考察
Firebaseのデータをカレンダーに表示することが出来ました。
次は気象APIから花粉情報を表示できるようにしたいと思います。
- 投稿日:2020-01-21T11:56:19+09:00
【エラー解消法】Vue.js + Karma + PhantomJS + Firebaseでテストしたときのエラー
掲題の環境で、unitテストを走らせたときのエラーについて解決法を書きます。
実行コマンド
npm run unit
エラー内容
error_log21 01 2020 10:41:54.753:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:9876/ 21 01 2020 10:41:54.757:INFO [launcher]: Launching browser PhantomJS with unlimited concurrency 21 01 2020 10:41:54.802:INFO [launcher]: Starting browser PhantomJS 21 01 2020 10:41:57.789:INFO [PhantomJS 2.1.1 (Windows 8.0.0)]: Connected on socket 91jkqAnyQjiCS3D5AAAA with id 48032196 PhantomJS 2.1.1 (Windows 8.0.0) ERROR ReferenceError: Can't find variable: Map at webpack:///node_modules/@firebase/app/dist/index.cjs.js:301:0 <- index.js:1043 PhantomJS 2.1.1 (Windows 8.0.0): Executed 0 of 0 ERROR (0.729 secs / 0 secs)
解決策
下記の記事を参考に、テストブラウザをPhantomJSからChromeHeadlessに変更しました。
https://dev.oro.com/posts/2017/07/programming/testing-with-headless-chrome/実行環境
- Microsoft Windows 10 Home ※仮想環境ではありません
- node v12.14.1
- npm v6.13.4
- package.jsonに記述したunitのスクリプト
cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run
エラー内容の解釈
Firebase特有のエラーかと思いましたが、そうではありませんでした。
ReferenceError: Can't find variable: Map
の意味がわからなかったので、この言葉だけでググるとStackOverFlowの記事がヒットしました。
https://stackoverflow.com/questions/43803151/referenceerror-cant-find-variable-map
Map
というのは、ES6から導入されたオブジェクトのことだそうです。
https://qiita.com/chihiro/items/9965cd7eca0380cf288cPhantomJSではサポートされてないオブジェクトのため、エラーとなったというわけです。
なお、polyfillで対応しようとしても別のエラーが出るので、おとなしくChromeHeadlessを使ったほうが良いと思いました。
https://stackoverflow.com/questions/41729482/phantomjs-cant-find-variable-mapなお、karma.conf.jsでframeworksにphantomjs-shimを入れていましたが、PhantomJSを使わなくなったので外しました。
感想
フロントの開発は、環境を理解するだけで時間が掛かります。
しかし、その分だけ、色んなツールが出て対応が早くなっているのも嬉しいことです。たくさん覚えることがありますが、コツコツ取り組んでいきます。
- 投稿日:2020-01-21T11:31:17+09:00
Vue.jsのmountedについて調べました
vue.jsを触る中で出た私の中での新出単語mountedを調べたので書いていきます!!
mountedとは
ライフサイクルフック
・・・mountedは・・・?(´・ω・`)となりますが、まあまあ(ノ´ー`)ノ読んでください
- vue.jsの初期化の決められたタイミングで実行される関数。初期化中に自動的に実行されるらしいです。
他にも
- Vueではインスタンスが作られてから、削除されるまでの間で、ライフサイクルフックを実行することができる
- 初期化の過程で、特定の段階でユーザー自身のコードを追加するために実行する関数
と説明されてました。
ちなみに公式ドキュメントのvue.jsのライフサイクル図は以下のようになっております。
ライフサイクルてなんやねん
て、私も最初思ってたんですけど、Vueのライフサイクルを完全に理解された方のQiitaをふむふむと読んで、なんとなく、vueのライフサイクルは、vueの中で実行される流れって感じなのかなと思いました。(語彙力
ということで
私の解釈だとライフサイクルフックは、インスタンス作成時、elementへのマウント時、データの更新時、インスタンスの削除時など、色々やるじゃないですか、その時にのイベント(?)というかタイミング(?)の名前?のことと解釈してます。
ちゅーことで
mountedは、vue.jsのライフサイクルの中でelementにマウントした後、というタイミングの話だったってことです、よね。
なんか間違ってたら教えてください。(´ー`)ノシ
- 投稿日:2020-01-21T11:11:57+09:00
Vue.js(Nuxt.js)で画像を表示させる方法(imgタグ、背景画像)
- 投稿日:2020-01-21T06:32:18+09:00
【Vue.js】表示後、3秒経過したら自動的に消えるフラッシュメッセージのサンプルコード
はじめに
表示後、3秒経過したら自動的に消えるフラッシュメッセージのサンプルコードを残します。
※単一ファイルコンポーネントにしていますので、コピペで挙動の確認が可能です。
環境
OS: macOS Catalina 10.15.1 Vue: 2.6.10結論:コード
※コード内に簡単な説明を記載しています。
Sample.vue<template> <div> <!-- v-ifで<script>内のshowがtrueであれば表示する --> <div class="flash" v-if="show === true" > this is flash message! </div> <!-- clickしたらmethods:{}内のshowFlashが発火する --> <button @click="showFlash" > show flash message </button> </div> </template> <script> export default { data() { return { show: false, } }, // Vueインスタンスに変化があったら発動する updated() { // setTimeoutで3000ms後にshowをfalseにする setTimeout(() => { this.show = false} ,3000 ) }, methods: { showFlash(){ this.show = true; } } } </script> <style> .flash { width: 200px; height: auto; } button { width: 200px; height: 30px; background: red; border-radius: 5px; } </style>おわりに
最後まで読んで頂きありがとうございました
どなたかの参考になれば幸いです
参考にさせて頂いたサイト(いつもありがとうございます)
- 投稿日:2020-01-21T00:51:06+09:00
【Vue.js】Vue.Draggableでドラッグ&ドロップ
ドラッグ&ドロップが実装できるライブラリを使ってみたので、メモです。
この記事の目標
- 項目ドラッグで、リストの入れ替えができるところまで
目次
環境
Vue.js 2.6.11
Vue.Draggable 2.23.2
1.導入
今回は yarn を使っているので、yarnでインストール
$ yarn add vuedraggablenpm だったら…
$ npm install -g vuedraggable2.全体
<template> <div class="draggable"> <h6 class="font-weight-bold">ポケモン</h6> <draggable element="ul" class="list-group col-4" :list="pokemons"> <li class="list-group-item" v-for="pokemon in pokemons" :key="pokemon.no"> {{ pokemon.name }} </li> </draggable> </div> </template> <script> import draggable from 'vuedraggable' export default { components: { draggable }, data () { return { pokemons: [ { no: '001', name: 'フシギダネ' }, { no: '004', name: 'ヒトカゲ' }, { no: '007', name: 'ゼニガメ' }, { no: '025', name: 'ピカチュウ' } ] } } } </script> <style scoped> li { cursor: pointer; } </style>12行目〜
- ライブラリを読み込んで、draggableコンポーネントとして使えるようにする
<script> import draggable from 'vuedraggable' export default { components: { draggable },4行目〜
draggableのタグで囲んだ部分が、ドラッグで動かせるようになる
- この例だと、li要素の部分
<draggable element="ul" class="list-group col-4" :list="pokemons"> <li class="list-group-item" v-for="pokemon in pokemons" :key="pokemon.no"> {{ pokemon.name }} </li> </draggable>
element="ul"
指定しないと<draggable>〜</draggable>
がdiv要素になる
- ↑ ul要素にしたいので、入れています
:list="pokemons"
この記述が無くても動作はする
- ↑ ドラッグで項目移動させた状態を配列に反映させたいので、入れています
3.参考
- 投稿日:2020-01-21T00:00:29+09:00
Vue.js で 変更したページから遷移する際、編集破棄確認メッセージを出力したい
色々とやっつけですが、書きました。
- (アンチパターンかと思いますが、)下記コードを mixinとしてまとめて使ってます。
- ページコンポーネントで、mixinをimportして使ってください。
- 適したタイミングで
addWatchEcentsFor
メソッドを呼びます。引数で渡した名前のプロパティの値が変更されたら、フラグが立ちます。- フラグが立った状態でページ遷移をしようとすると、「よろしいですか?」のメッセージが出ます。
confirmEditDiscard.jsexport default { data() { return { //対象オブジェクトが変更されたか isWatchPropChanged: false }; }, /** * 対象オブジェクトの変更監視イベントを追加する。 */ methods: { addWatchEventsFor(watchPropName) { this.$watch( watchPropName, // 変更があったらフラグを立てる function() { this.isWatchPropChanged = true; }, { deep: true } ); } }, beforeRouteLeave(to, from, next) { // 確認画面へは、チェックせずページ遷移 const componentName = this.$options.name; if (componentName === to.name.replace("Confirm", "")) { next(); return; } // 変更がない場合も、チェックせずページ遷移 if (!this.isWatchPropChanged) { next(); return; } window.confirm("編集データは破棄されます。よろしいですか?") ? next() : next(false); } };