20190829のvue.jsに関する記事は15件です。

初めてのvue.js 簡易的なメモ・備忘録編

はじめに

まず、私の説明だけ簡単にさせていただきます。
とあるベンチャー企業でフロントエンドエンジニアとして働いています。
ライブラリーはReactを使い、1年にも満たないですが、redux,redux-saga,非同期通信などなど、様々なことに挑戦して、Reactを自信を持って書けるようになりました。
そんな私が、あるきっかけでvue.jsを使用することになってので、勉強録としてまとめてみました。

今回は、まとめというより、メモという意味合いが強いです。
vue.jsやフロントエンドに強い方、謝りや誤認識等ありましたら、ぜひご指摘ください!

こちらの書籍をベースに学習しております。
Reactに馴染んでいる私にとって、非常にわかりやすく、スラスラ読める一冊です!
初めてプログラミングに触れた時は、全く理解できなかったが………
Vue.js入門 基礎から実践アプリケーション開発まで

それでは。

オプション(メソッドのようなもの)

data

UIの状態・データ
Reactでいうstateみたいなものかな?変数のような概念かな?
この data を通して、html側にデータを送るようです。
名前から役割が想像しやすいですね。

el

vueインスタンスをマウントする要素。
マウントがよく分かっていません。
elでマウントすることで、該当htmlにデータを送受信したり、計算したり、加工したりできるようになる。

メソッドによるマウントもでき、その場合は$mountを使うようです。

filters

データを文字列と整形する。
データを 計算 するのではなく、あくまでも整形する。
後述ののcomputedと役割が被って見えます。

methods

イベントが発生した時などの振る舞い。
onClick等の処理を書いているところですね。

computed

データから派生して算出される値。
要は、受け取ったデータを 計算 しているものですね。
前述のfiltersと役割が被って見えます。

テンプレート構文

{{}} Mustache構文(二重中括弧)という名前らしいです。

ディレクティブ

オプションと同じように、vue.jsで重要な概念の一つ。
ディレクティブにより、onClickで処理をメソッドに渡したり、繰り返しの構文を使えたり、formのデータを取得できるようです。

v-bind

`v-bind: 属性名 = "データを展開した属性値"
buttonのdisableやstyleなどに、vueのデータやメソッドを挟み込めるようになりますね。

省略記法として、: + 属性名で記述できます。

<p v-bind:class="hoge"><p :class="hoge">

v-if/v-show

条件付きレンダリング。
名前だけやと分からないですね。
非表示・非表示を切り替える場合に使うものです。

見た目上は同じような動きをしますが、v-ifはDOMの追加・削除を、v-showはdisplayプロパティの変更をするようです。

v-for

リストレンダリング。
jsのfor構文と同じく、html上でデータの繰り返し処理ができるようです。

v-on

イベントハンドリング。
onClickやonChange属性にメソッドや処理を渡せるようです。

省略記法として@があります。

<button v-on:click="hoge"><button @click="hoge">

v-model

フォーム入力バインディング。
双方向バインディングを実現できる。
Reactでは少し記述が冗長になるため、これは非常に便利!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vee-validate で「特定のフィールドに固有のバリデーションメッセージを設定する

はじめに

「特定のフィールドの regex バリデーションメッセージを変更したい」という要件が上がってきたんだけど、vee-validateではどう実現するのが正解がわからなかった。

やりたいこと

  • regex のグローバルで設定しているバリデーションメッセージは変更したくない。
  • 特定のフィールドの regex のバリデーションメッセージだけを変更したい。
  • 他の検証ルールのメッセージはそのまま。

やったこと

lib/validatormessage.ts
import { Validator } from 'vee-validate';

export function getError(
    validator: Validator,
    field: string,
    ruleMsssageMap: { [rule: string]: string }
): string | null {
    const errors = validator.errors.items.filter((e) => e.field === field);
    if (errors.length > 0) {
        const error = errors[0];
        let isExists = false;
        for (const rule in ruleMsssageMap) {
            if (error.rule === rule) {
                isExists = true;
            }
        }

        if (isExists) {
            return ruleMsssageMap[error.rule];
        } else {
            return validator.errors.first(field);
        }
    }

    return null;
}
xxx.vue
<template>
  <input
    type="text"
    name="myfield"
    v-model="myfieldValue"
    v-validate="{ required: true, regex: /[1-9]/ }"
  />
  <div v-show="myfieldError">
    {{ myfieldError  }}
  </div>
</template>
<script lang="ts">
import { getError } from '@lib/validatormessage'
...
computed: {
    myfieldError: {
      get: (): string | null => {
        return getError(this.$validator, 'myfield' { regex: 'cutome error message.' })
      },
    },
  },
<script>

まとめ

特定のフィールドで、他の検証ルールに影響をあたえず、特定の検証ルールにのみバリデーションメッセージを設定することに成功しました。
みんなはどのように実装しているんだろう…(゜_゜)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsのローカル環境での使用方法

初めに

 とあるインターン先でVue.jsを使うことになり、その勉強のメモのために記事を投稿してみました。
まず、Vue.jsとはJavascriptのフレームワークのことで、個人的な意見として、他のフレームワークに比べて内容が理解しやすいと感じました。
 Vue.jsの勉強にはjsfiddleという無料で使えるサイトを使う場合もありますが、今回は自分のテキストエディタで使用する方法を解説していきます。

方法

 まずは、Vue.jsの公式サイトにアクセスし、ページ内でインストールと検索してください。その後、下のほうに移動すると、直接読み込み
あるため、開発バージョンをダウンロードしてください。
 ここからは、エディタを使っていきます。先ほどダウンロードしたものと同じ場所にHTMLファイルを作成します。そして、そのHTMLファイルのbodyタグ内の1番初めに、
<script src = "vue.js"></script>
bodyタグ内の最後にをいれ、この中にVue.jsのコードを書いていきます。例として、

new Vue({
el: '#app',
data: {
url: 'https://jp.vuejs.org/',
toggle: true,
languages: ['Javascript' , 'Ruby' , 'Python']
}
})

と記述します。では、実際に動くか確かめてみましょう。先ほど記述した2つのコードの間に
```



Vue.js

<ul>
    <li v-for = "language in languages">
        {{ language }}
    </li>
</ul>

<p>
    <input type = "text" v-model = "message">
</p>


```
と入れると、結果のように表示されるので、成功してといえます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

inline要素の改行のスペースが気になったのでvueのカスタムディレクティブで解決してみた

これはなに

inline要素内で改行するとスペースが入りこむので、見た目的に気になったりする

https://codepen.io/aokiken/pen/vYBLMyv

<div>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>

// 出力結果
// 1 2 3 4 5

// 意図しないスペースを○で再現
// 1○2○3○4○5

対応策

改行しなければスペースは入らない

<div>
    <span>1</span><span>2</span><span>3</span><span>4</span><span>5</span>
</div>

閉じタグの>を改行ればスペースは入らない

<div>
  <span>1</span
  ><span>2</span
  ><span>3</span
  ><span>4</span
  ><span>5</span>
</div>

開始タグの<tag以下を改行ればスペースは入らない

<div>
  <span
    >1</span><span
    >2</span><span
    >3</span><span
    >4</span
    ><span
    >5</span>
</div>

コメントアウトを挟めばスペースは入らない

<div>
    <span>1</span><!--
--><span>2</span><!--
--><span>3</span><!--
--><span>4</span><!--
--><span>5</span>
</div>

jsで改行を取り除けばスペースはなくなる

<div id="js_remove_nl">
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>
<script>
const removeWhiteSpaceDom = document.querySelector('#js_remove_nl')
const removeWhiteSpaceHtml = removeWhiteSpaceDom.innerHTML.replace(/\s*<("[^"]*"|'[^']*'|[^'">])*>\s*/g, match => match.trim())
removeNlDom.innerHTML = removeWhiteSpaceHtml
</script>

cssでfont-sizeを0にすれば見た目上スペースはなくなる(コピペにスペースは入り込む)

<div class="remove_whitespace">
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>
<style>
.remove_whitespace {
  font-size: 0;
}
.remove_whitespace > span {
  font-size: initial;
}
</style>

float: left(コピペにスペースは入り込む)

<div class='float_left'>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>
<style>
.float_left:after {
  content: "";
  display: block;
  clear: both;
}
.float_left > span {
  float: left;
}
</style>

display: flex(コピペに段落は入り込む)

<div class='flex'>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>
<style>
.flex {
  display: flex
}
</style>

vueのカスタムディレクティブで解決してみる

https://www.npmjs.com/package/vue-remove-whitespace

<div v-remove-whitespace>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>

// 出力結果
// 12345

余談

英語圏だと、単語の間にスペースが入るのが基本だから、問題視されないのかなーという

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

inline要素の改行のスペースが気になったのでvueのカスタムディレクティブ作成して解決してみた

これはなに

inline要素内で改行するとスペースが入りこむので、見た目的に気になったりする

https://codepen.io/aokiken/pen/vYBLMyv

<div>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>

// 出力結果
// 1 2 3 4 5

// 意図しないスペースを○で再現
// 1○2○3○4○5

vueのカスタムディレクティブで解決してみる

https://www.npmjs.com/package/vue-remove-whitespace

<div v-remove-whitespace>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>

// 出力結果
// 12345

その他の対応策

改行しなければスペースは入らない

<div>
    <span>1</span><span>2</span><span>3</span><span>4</span><span>5</span>
</div>

閉じタグの>を改行ればスペースは入らない

<div>
  <span>1</span
  ><span>2</span
  ><span>3</span
  ><span>4</span
  ><span>5</span>
</div>

開始タグの<tag以下を改行ればスペースは入らない

<div>
  <span
    >1</span><span
    >2</span><span
    >3</span><span
    >4</span
    ><span
    >5</span>
</div>

コメントアウトを挟めばスペースは入らない

<div>
    <span>1</span><!--
--><span>2</span><!--
--><span>3</span><!--
--><span>4</span><!--
--><span>5</span>
</div>

jsで改行を取り除けばスペースはなくなる(カスタムディレクティブでも同様のことやってる)

<div id="js_remove_nl">
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>
<script>
const removeWhiteSpaceDom = document.querySelector('#js_remove_nl')
removeNlDom.innerHTML = removeWhiteSpaceDom.innerHTML.replace(/\s*<("[^"]*"|'[^']*'|[^'">])*>\s*/g, match => match.trim())
</script>

cssでfont-sizeを0にすれば見た目上スペースはなくなる(コピペにスペースは入り込む)

<div class="remove_whitespace">
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>
<style>
.remove_whitespace {
  font-size: 0;
}
.remove_whitespace > span {
  font-size: initial;
}
</style>

float: left(コピペにスペースは入り込む)

<div class='float_left'>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>
<style>
.float_left:after {
  content: "";
  display: block;
  clear: both;
}
.float_left > span {
  float: left;
}
</style>

display: flex(コピペに段落は入り込む)

<div class='flex'>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>
<style>
.flex {
  display: flex
}
</style>

余談

英語圏だと、単語の間にスペースが入るのが基本だから、問題視されないのかなーという

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

inline要素の改行のスペースが気になったので対策を調べた

これはなに

inline要素内で改行するとスペースが入りこむので、見た目的に気になったりする

https://codepen.io/aokiken/pen/vYBLMyv

例えば、以下のような、文章の途中のリンクなどに困る

<p>
  お問い合わせは
  <a href="#">こちらから</a>
  お願いします
</p>

// 出力結果
// お問い合わせは こちらから お願いします

サンプルはあくまで例だが、前後の文章やaタグにclassやattributeなど、内容によっては1行で表現しづらい

vueのカスタムディレクティブを作って解決してみる

https://www.npmjs.com/package/vue-remove-whitespace

<div v-remove-whitespace>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>

// 出力結果
// 12345

その他の対応策

改行しなければスペースは入らない

<div>
    <span>1</span><span>2</span><span>3</span><span>4</span><span>5</span>
</div>

閉じタグの>を改行ればスペースは入らない

<div>
  <span>1</span
  ><span>2</span
  ><span>3</span
  ><span>4</span
  ><span>5</span>
</div>

開始タグの<tag以下を改行ればスペースは入らない

<div>
  <span
    >1</span><span
    >2</span><span
    >3</span><span
    >4</span
    ><span
    >5</span>
</div>

コメントアウトを挟めばスペースは入らない

<div>
    <span>1</span><!--
--><span>2</span><!--
--><span>3</span><!--
--><span>4</span><!--
--><span>5</span>
</div>

jsで改行を取り除けばスペースはなくなる(カスタムディレクティブでも同様のことやってる)

<div id="js_remove_nl">
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>
<script>
const removeWhiteSpaceDom = document.querySelector('#js_remove_nl')
removeNlDom.innerHTML = removeWhiteSpaceDom.innerHTML.replace(/\s*<("[^"]*"|'[^']*'|[^'">])*>\s*/g, match => match.trim())
</script>

cssでfont-sizeを0にすれば見た目上スペースはなくなる(コピペにスペースは入り込む)

<div class="remove_whitespace">
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>
<style>
.remove_whitespace {
  font-size: 0;
}
.remove_whitespace > span {
  font-size: initial;
}
</style>

float: left(コピペにスペースは入り込む)

<div class='float_left'>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>
<style>
.float_left:after {
  content: "";
  display: block;
  clear: both;
}
.float_left > span {
  float: left;
}
</style>

display: flex(コピペに段落は入り込む)

<div class='flex'>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
</div>
<style>
.flex {
  display: flex
}
</style>

余談

英語圏だと、単語の間にスペースが入るのが基本だから、問題視されないのかなーという

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ionic + vueでqr-scannerを使いたい時のメモ(自分用)

https://qiita.com/reon777/items/82f6292248d76d1f3360
ここでvue.jsにcordovaをインストールする

*ここはかなり手こずった。

*cordovaをインストールする場所に要注意

https://github.com/ionic-jp/capacitor-docs/blob/master/site/docs-md/cordova/using-cordova-plugins.md

*capacitorでcordovaのプラグインを使う時の方法

$ npm install @ionic-native/qr-scanner
$ npm install cordova-plugin-qrscanner
$ npx cap sync

この時エラー文大量に出てきたけど無視して行った。

https://ionicframework.com/docs/native/qr-scanner

これとは微妙に書き方が違うので注意

qrscan.vue
hoge() {
      const qrScanner = new QRScanner(); //新しくインスタンスを生成
// qrScanner.変数名でいろんな機能を使えるようになった
      qrScanner.prepare().then((status) => {
        if (status.authorized) {
          console.log('hogehoge')
          qrScanner.show();
          const scanSub = qrScanner.scan().subscribe((text: string) => {
            console.log(text);
            qrScanner.hide();
            scanSub.unsubscribe();
          });
        }
      }).catch((e) => {
        console.log(e);
      });
    },
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】Vuejsをchromeブラウザでデバッグする方法

開発環境
⭐️Mac book pro (Mac OS Mojave)
⭐️ブラウザ Google Chrome

https://twitter.com/nonnonkapibara/status/1166711295384510465




Vuejsをchromeブラウザでデバッグする方法

1.Chromeのアドオン「Vue.js Devtools」を入れる。
Vue.js Devtools
https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd
スクリーンショット 2019-08-29 18.52.46.png

2.インストールが終わったらVue.js Devtoolsのアイコンを右クリックして「拡張機能を管理」を開く
スクリーンショット 2019-08-29 18.55.38.png

3.「Vue.js Devtools」ONにして、「ファイルのURLへのアクセス許可する」をONにする
スクリーンショット 2019-08-29 18.56.51.png
スクリーンショット 2019-08-29 18.57.30.png

main.jsの上部に

Vue.config.devtools = true;

を追記する
スクリーンショット 2019-08-31 1.51.26.png

4.Vue.jsページをChromeで開くと、Vue.js Devtoolsアイコンが有効になる。緑色がつく。
スクリーンショット 2019-08-29 18.59.51.png

5.画面の右クリックで「検証」を選ぶ
スクリーンショット 2019-08-29 19.01.15.png

6.上のタブに「Vue」が追加されているので、選択する
スクリーンショット 2019-08-29 19.02.27.png

7.データの内容が見る事ができた。
スクリーンショット 2019-08-29 19.04.10.png

こんな感じにデバッグできる。
スクリーンショット 2019-08-31 1.54.45.png

完了

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】rails webpacker:install に失敗する場合の対処法

概要

Rails 5.1 + Vue.js で開発を行う - part1 環境構築を参考に構築済みのrails環境にvue.jsを追加しようとしていた。
webpackerをインストールするため、$rails webpacker:installを実行したらコケる現象に遭遇したのでその解消法を記載する。

環境

  • ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin18]
  • Rails 5.2.3
  • yarn 1.17.3

rails webpacker:install した時次のエラーが出力される

Errno::ENOENT: No such file or directory @ rb_sysopen - /Users/hoge/projects/testapp/config/webpacker.yml

どうやらtestapp/config配下にwebpacker.ymlがないことが原因らしい。
webpacker.ymlを生成するために$ rails webpacker:install
してるのに....

apple-no-MBP:testapp hoge$ rails webpacker:install


/Users/hoge/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/twitter-4.8.1/lib/twitter/cursor.rb:51: warning: circular argument reference - collection
Faraday::Builder is now Faraday::RackBuilder.
rails aborted!
Don't know how to build task 'webpacker:install
' (See the list of available tasks with `rails --tasks`)
Did you mean?  webpacker:install
               webpacker:install:vue
               webpacker:install:erb
               webpacker:install:elm
               webpacker:binstubs
/Users/hoge/projects/testapp/bin/rails:9:in `<top (required)>'
/Users/hoge/projects/testapp/bin/spring:15:in `<top (required)>'
bin/rails:3:in `load'
bin/rails:3:in `<main>'
(See full trace by running task with --trace)
apple-no-MBP:testapp hoge$ bundle exec rails webpacker:install
/Users/hoge/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/twitter-4.8.1/lib/twitter/cursor.rb:51: warning: circular argument reference - collection
Faraday::Builder is now Faraday::RackBuilder.
/Users/hoge/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/twitter-4.8.1/lib/twitter/cursor.rb:51: warning: circular argument reference - collection
Faraday::Builder is now Faraday::RackBuilder.
RAILS_ENV=development environment is not defined in config/webpacker.yml, falling back to production environment
rails aborted!
Webpacker configuration file not found /Users/hoge/projects/testapp/config/webpacker.yml. Please run rails webpacker:install Error: No such file or directory @ rb_sysopen - /Users/hoge/projects/testapp/config/webpacker.yml
/Users/hoge/projects/testapp/config/environment.rb:5:in `<top (required)>'
/Users/hoge/projects/testapp/bin/rails:9:in `<top (required)>'
/Users/hoge/projects/testapp/bin/spring:15:in `require'
/Users/hoge/projects/testapp/bin/spring:15:in `<top (required)>'
./bin/rails:3:in `load'
./bin/rails:3:in `<main>'

Caused by:
Errno::ENOENT: No such file or directory @ rb_sysopen - /Users/hoge/projects/testapp/config/webpacker.yml
/Users/hoge/projects/testapp/config/environment.rb:5:in `<top (required)>'
/Users/hoge/projects/testapp/bin/rails:9:in `<top (required)>'
/Users/hoge/projects/testapp/bin/spring:15:in `require'
/Users/hoge/projects/testapp/bin/spring:15:in `<top (required)>'
./bin/rails:3:in `load'
./bin/rails:3:in `<main>'
Tasks: TOP => app:template => environment
(See full trace by running task with --trace)
apple-no-MBP:testapp hoge$ 
apple-no-MBP:testapp hoge$ 
apple-no-MBP:testapp hoge$ bundle exec rails webpacker:install
/Users/hoge/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/twitter-4.8.1/lib/twitter/cursor.rb:51: warning: circular argument reference - collection
Faraday::Builder is now Faraday::RackBuilder.
/Users/hoge/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/twitter-4.8.1/lib/twitter/cursor.rb:51: warning: circular argument reference - collection
Faraday::Builder is now Faraday::RackBuilder.
RAILS_ENV=development environment is not defined in config/webpacker.yml, falling back to production environment
rails aborted!
Webpacker configuration file not found /Users/hoge/projects/testapp/config/webpacker.yml. Please run rails webpacker:install Error: No such file or directory @ rb_sysopen - /Users/hoge/projects/testapp/config/webpacker.yml
/Users/hoge/projects/testapp/config/environment.rb:5:in `<top (required)>'
/Users/hoge/projects/testapp/bin/rails:9:in `<top (required)>'
/Users/hoge/projects/testapp/bin/spring:15:in `require'
/Users/hoge/projects/testapp/bin/spring:15:in `<top (required)>'
./bin/rails:3:in `load'
./bin/rails:3:in `<main>'

Caused by:Errno::ENOENT: No such file or directory @ rb_sysopen - /Users/hoge/projects/testapp/config/webpacker.yml
/Users/hoge/projects/testapp/config/environment.rb:5:in `<top (required)>'
/Users/hoge/projects/testapp/bin/rails:9:in `<top (required)>'
/Users/hoge/projects/testapp/bin/spring:15:in `require'
/Users/hoge/projects/testapp/bin/spring:15:in `<top (required)>'
./bin/rails:3:in `load'
./bin/rails:3:in `<main>'
Tasks: TOP => app:template => environment
(See full trace by running task with --trace)

エラー遭遇までに実行したことなど

rails環境にvue.jsを導入する時はたぶん以下の手順が一般的かも

  1. Gemfileにgem 'webpacker', github: 'rails/webpacker'を書き込んで$bundle install →OK
  2. yarnのインストール。$ brew install yarn or $brew upgrade yarn →OK
  3. webpackerのインストール。$ rails webpacker:install
 →エラー発生

対処法

$ rails webpacker:installする前にwebpacker.ymlをつくった。めちゃくちゃ野蛮な解決法。
一応同様のissueはすでに上がっており、そこに載ってた対処法。
https://github.com/rails/webpacker/issues/940

以下のソースコードをコピペ→config配下にwebpacker.ymlをつくってその中にペースト。
https://raw.githubusercontent.com/rails/webpacker/master/lib/install/config/webpacker.yml

おしまい。たぶんyarnのバージョンが原因な気はしている。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker+Vue.js+Vuetify環境構築

この記事について

VSCodeのRemoteを使って、Dockerコンテナー内に、Vue.js+Vuetify環境を構築するためのメモです。

対象

  • VSCodeはある程度使い慣れている
  • VSCodeのRemote container拡張機能は使った事がある

dockerfile作成

EXPOSE 9000でポート指定をする事を忘れないように。
最悪コピペでもOK

dockerfile
FROM node:lts

WORKDIR /app

RUN npm install -g npm && \
    npm install -g @vue/cli

EXPOSE 9000

CMD ["/bin/sh"]

dockerビルド

VSCode Remote Containerを使用して、ビルドを行う。

Vue-Cliインストール

shell
$ npm install -g @vue/cli

Vueプロジェクト作成

コマンドを叩いた後に、途中で選択肢が出るので、defaultUse NPMを選択

shell
$ vue create sandbox

> default (babel, eslint)
> Use NPM

VSCode RemoteでPORT設定

Forewd Port from Containerを選択し、8080を入力

Vueの動作確認

shell
$ cd sandbox
$ npm run serve

コマンドを叩いた後に、localhostにアクセス
http://localhost:8080/studio

動作確認できたら、ctrl+cで一旦終了。

Vuetify インストール

コマンドを叩いた後に、選択肢があるのでDefault (recommended)を選択

shell
$ vue add vuetify

> Default (recommended)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GitHub PagesにDocker+Vue.js+Vuetifyでページを公開

この記事について

VSCodeのRemoteを使って、Dockerコンテナー内に、Vue.js+Vuetify環境を構築するためのメモです。

対象

  • VSCodeはある程度使い慣れている
  • VSCodeのRemote container拡張機能は使った事がある

dockerfile作成

EXPOSE 8080でポート指定をする事を忘れないように。
最悪コピペでもOK

dockerfile
FROM node:lts

WORKDIR /app

RUN npm install -g npm && \
    npm install -g @vue/cli

EXPOSE 8080

CMD ["/bin/sh"]

dockerビルド

VSCode Remote Containerを使用して、ビルドを行う。

Vue-Cliインストール

shell
$ npm install -g @vue/cli

Vueプロジェクト作成

カレントディレクトリにvueプロジェクトを作成する

コマンドを叩いた後に、途中で選択肢が出るので、yes> default>Use NPMを選択

shell
$ vue create .
> Y
> default (babel, eslint)
> Use NPM

VSCode RemoteでPORT設定

Forewd Port from Containerを選択し、8080を入力

Vueの動作確認

shell
$ npm run serve

コマンドを叩いた後に、localhostにアクセス
http://localhost:8080/studio

動作確認できたら、ctrl+cで一旦終了。

Vuetify インストール

コマンドを叩いた後に、選択肢があるのでDefault (recommended)を選択

shell
$ vue add vuetify

> Default (recommended)

ビルドディレクトリ変更

/node_modules/@vue/cli-service/lib/options.js

options.js
<省略>

exports.defaults = () => ({
  // project deployment base
  publicPath: '',
  // for compatibility concern. TODO: remove in v4.
  baseUrl: '',

  // where to output built files
  //  outputDir: 'dist',
  outputDir: 'docs',

  assetsPublicPath: '',

  // where to put static assets (js/css/img/font/...)
  assetsDir: '',

  // filename for index.html (relative to outputDir)
  indexPath: 'index.html',


ビルド

docsディレクトリにビルドされたファイルが格納されるので、そのままGitHub Pagesにデプロイできます。

shell
$ npm run build

Vue routerを入れる

shell
$ vue add router
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

30分でわかるVue.js入門

目的

フロントエンドのJSを知らないので、どれでもいいので理解したい。
フロントエンドで主流は廃れたJQueryを除くと3つある。
1. Angular.js
2. React.js
3. Vue.js
このなかで、Github上で一番人気があるのがVue.jsである。
なので、Vue.jsのザックリ理解するために入門編としてまとめてみた。

1.Vue.jsとは

 JavaScriptフレームワークの1つで、MVCモデルのView層を扱います。
 読み方:ビュードットジェイエス
 特徴:学習コストが低い

 ※スペルを間違えやすいので、声に出すと間違えない。
 ○:ブイユーイー
 ✗:ブイイーユー

対象ブラウザ:ECMAScript5(エクマスクリプト)をサポートするブラウザ。

      簡単に言うと、最近のJavaScript標準をサポートするブラウザ。→Chrome,IE9以降

2.ツール

 Vue DevtoolsというChromeの拡張機能が公開されている。
https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=ja&
検索ワード:vue chrome プラグイン

 Nuxt.jsという便利なフレームワークも提供されており、そちらを使うようになってからは
もっぱらVSCodeを利用している。

VSCode
https://azure.microsoft.com/ja-jp/products/visual-studio-code/

VSCode日本語化
https://qiita.com/ohno888/items/b9e955a2ab1ad0e09767

3.セットアップ

 Vueはライブラリなので、downloadして読み込ませるか、CDNを利用する。
 https://jp.vuejs.org/v2/guide/installation.html
 以下、CDNが使えるのでそちらを利用して開発できる。
 ※CDNとはコンテンツデリバリーネットワークの略で、大容量のデジタルコンテンツをインターネット上で大量配信するためのネットワークのこと。

4.さっそくHello World

hello.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue.js</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
    <!--Vueデータを出力する-->
    <div id="app">{{ msg }}</div>

    <script>
    // id=appの要素にVueを適用する
    const app = new Vue({
        el:'#app',
        data:{ msg:'Hello World!' }
    })
    </script>
</body>
</html>

解説

(1){{プロパティ名}}でデータ出力する。
(2)elプロパティはオブジェクトを割り当てる。キー名:値の形式で指定する。

作法だけ覚えれば簡単。チートシートなどを用意すれば誰でも書ける。

5.次はイベントハンドリング(ボタンアクション)

event.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue.js</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
    <!--Vueデータを出力する-->
    <div id="app">
        <button v-on:click="changeMsg">change</button>
        <p>{{ msg }}</p>
    </div>

    <script>
    // id=appの要素にVueを適用する
    const app = new Vue({
        el:'#app',
        data:{ msg:'hoge' },
        methods:{
            changeMsg:function(){
                this.msg = 'fuga'
            }
        }
    })
    </script>
</body>
</html>

解説

「v-on:DOMイベント」でイベントハンドリングが実装できる。
methodsプロパティに呼び出されるメソッドを記述する。

6.コンポーネント

component.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue.js</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
    <!--Vueデータを出力する-->
    <div id="app">
        <my-comp></my-comp>
    </div>

    <script>
    Vue.component('my-comp',{
        template:'<h1>hogehoge</h1>'
    })
    var app = new Vue({
        el:'#app',
    })
    </script>
</body>
</html>

解説

コンポーネントは「Vue.component()」で定義します。
templateプロパティにコンポーネントに含める要素を指定します。

7.ディレクティブ

Vue.jsではHTMLタグに「v-**」の属性を指定することができ、これを「ディレクティブ」と呼びます。
ディレクティブを使いこなすことが、Vue.jsを使いこなす第一歩と言えます。

代表的なディレクティブ

v-text テキストをレンダリングする

v-html HTMLをレンダリングする。タグも変換しないで出力します。
でタグもHTMLで出力できます。rawHTMLが値

v-show 条件に応じたレンダリング制御を行う
 CSSのdisplayプロパティで表示を制御

v-if,v-else,v-else-if 条件分岐を行う
 要素自体の有無を制御

v-for 繰り返しを行う

v-on イベントハンドリングを実装する
 @で記述を省略することができます。

v-bind HTML属性を割り当てる
 :で記述を省略することができます。

v-model 入力フォームへモデルを割り当てる
 input,select,textareaに対してVueオブジェクトのデータとフォーム部品の値を関連付けます。

v-pre テキストをそのまま出力する

v-once 一度だけレンダリングする
 レンダリングを1度だけ行い、それ以降は変数が変動しても、表示に反映させなくします。

8.最後は実践例(リアルタイム入力チェック)

validation.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Vue.js</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <style>
        #app .error{
            color: red;
        }
    </style>
</head>
<body>

    <!--Vueデータを出力する-->
    <div id="app">
        <input type="text" v-model="msg">
        <div class="error" v-if="error.require">必須項目です</div>
        <div class="error" v-if="error.tooShort">短すぎます</div>
        <div class="error" v-if="error.tooLong">長すぎます</div>
    </div>

    <script>
    const app = new Vue({
        el:'#app',
        watch:{
            msg:function(newVal, oldVal){
                this.error.require = (newVal.length < 1)?true:false;
                this.error.tooShort = (newVal.length < 3 && newVal.length > 0)?true:false;
                this.error.tooLong = (newVal.length > 5)?true:false;

            }
        },
        data:{
            msg:'hoge!',
            error: {
                require: false,
                tooShort: false,
                tooLong: false
            }
        }
    })
    </script>
</body>
</html>

解説と注意点

watchは、入力値を監視するためのオプション。

Vueのメソッドやプロパティはカンマでつなぐ必要あり。忘れやすいので注意。上でいうとdataの前のカンマ
app .errorの指定で、appの後ろに空白が必要


以上でVue.jsの入門編は終わり。
他にもVue RouterやVuexなど色々あるが、
まずは上記を押さえれば入門者としては良いと考えています。

今後の記載予定

入門編:当記事
初級者編:Vue CLI、Vue Router、Vuexなど
中級者編:Nuxt.jsなどを利用し他システムとの連携

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vuetify on Nuxt.js】実際に本番運用したコンポーネントとそのカスタマイズ

概要

本スライドはVuetify Meetup #1での発表資料です。面白そうと思った方はぜひ次回以降参加してください!LTも募集中です。


自己紹介


業務内容

Twitter @Meijin_garden
名人って呼ばれてます。

株式会社NoSchoolのCTO。
中高生のための勉強質問サイトを作ってます。
自分しかフルタイムのエンジニアがいないのでAWSもFirebaseもLaravelもNuxt.jsもデザインも採用も機械学習もやってます。


経歴

奈良高専情報工学科

株式会社LIFULL

株式会社NoSchool


Vuetify on Nuxt.jsを採用した理由


背景

2017年からNoSchoolのサイト自体はあるが、社長がWordPress on SakuraServerで起業したのでエンジニアから見たら酷いものだった。
シードの資金調達を終えて僕がジョインした2019年3月から5月にかけて、フルリプレイスを行うこととなった。


リプレイス後の技術構成

インフラ: AWS(CloudFront, ALB, EC2, S3...)
サーバーサイド: Laravel
フロントエンド: Nuxt.js, jQuery

※2ヶ月じゃフロントまで移行やりきることができず、現在もページ単位でNuxtに移行中


Nuxt.jsの採用理由

  • 僕自身がVue.jsの経験が1年強あった(前職の新規事業で1人でフルスクラッチした)
  • jQueryと比較して開発速度が段違い
  • ReactとVueだとあまり違いも採用力が衰えるということもなさそうなのでCTOの僕が経験があって開発速度がとにかく速いものを選べばいいやという考え

Vuetifyの採用理由

  • デザイナー不在でそこそこの品質で開発をするためにUIフレームワークは必須
  • Bulma、Bootstrap Vueなども見たが、Class属性ではなくコンポーネント自体を用意してくれている方が使い勝手がいいと思った
  • ちなみにElementは前職で使っていたけどレスポンシブや細かいところでイライラした

実際にVuetifyで頑張ったページたち(2019/8時点)


家庭教師一覧

スクリーンショット 2019-08-29 10.41.43.png


家庭教師詳細

スクリーンショット 2019-08-29 10.47.45.png


質問作成

FireShot Capture 001 - NoSchool - NoSchool 無料で勉強の質問、塾や家庭教師の検索 - noschool.asia.png


Vuetifyでまずこれだけ覚えておけば的なコンポーネントリスト


  • ページ(v-app, v-content等)
  • レイアウト(v-layout, v-flex
  • 余白(.py-2, .mx-4等)
  • 装飾系(v-card, v-chip, v-icon
  • 画像系(v-img, v-avatar
  • フォーム(v-btn, v-select, v-text-field等)
  • あとはドキュメントを読み込む。カスタムSlotも便利。

Vuetifyの便利コンポーネントをいくつか紹介


ページ構成(default.vue)

<template lang="pug">
v-app
  my-header
  v-content(app)
    my-breadcrumbs(:items="breadcrumbs")
    v-container(app fluid)
      nuxt
  my-footer
</template>

※ちょっと改変はしてます

v-appv-contentなどは公式ドキュメントの通り。


パンくず(Breadcrumbs.vue)

パンくずはv-breadcrumbsがベースで、カスタマイズのためにv-slot:itemを利用。

<template lang="pug">
  v-breadcrumbs(:items="items").px-2.py-0.grey.lighten-3
    template(v-slot:item="props")
      v-breadcrumbs-item(
        :to="props.item.to"
        :disabled="props.item.disabled"
        v-if="props.item.to !== '/'"
      )
        span.grey--text {{ props.item.text }}
      span(v-else)
        a(href="/").pa-0
          v-icon.body-1.primary--text mdi-{{ props.item.icon }}
    template(v-slot:divider)
      span >
</template>

↓こうなります
スクリーンショット 2019-08-29 10.58.10.png


ページネーション

<template lang="pug">
  v-pagination(
    v-model="pageModel"
    @input="onPaginationClicked"
    @next="onPaginationClicked"
    @previous="onPaginationClicked"
    :length="last_page"
    :total-visible="7"
    style="overflow-x: scroll;"
  ).my-4
</template>

scriptを抜粋するとこんな感じ。

<script>
export default {
...
  methods: {
    onPaginationClicked(event) {
      this.$emit("query-updated", {
        page: this.pageModel
      });
    }
  }
...
};
</script>

要は、ページネーションのページ番号が変わったときに呼び出し元コンポーネントで再度API叩くなりする必要があるので、親にemitする設計にしている。


ページネーションの注意

total-visibleが偶数だと、中央あたりのページ番号で表示が変になるので注意。
例えば6にしたとき、全ページ数が10などのときに5ページ目付近で表示がおかしくなり、隣り合ったページが表示されない。
これは...も含めたvisibleのcountだから、偶数だと...の数が合わなくて隣り合ったページが出ない。
(気になるなら直せやOSSだろという話ですよねすみません)


Vuetifyをベースにカスタマイズしているコンポーネント


見出し系

Vuetifyの見出しはフォントサイズが大胆なのと、サイト全体で統一感を出したいので共通コンポーネント化。

plugin/globalComponent.js
import Vue from 'vue'

import Body from "@/components/layouts/Body";
import PageTitle from "@/components/parts/heading/pageTitle";
import SubTitle from "@/components/parts/heading/subTitle";
import SubTitle2 from "@/components/parts/heading/subTitle2";

Vue.component('sec-main', Body)
Vue.component('page-title', PageTitle)
Vue.component('subtitle', SubTitle)
Vue.component('subtitle2', SubTitle2)

悩ましいのは、マージンまでコンポーネントに入れるかどうか。入れるとしたらどれくらい大きいマージンを入れるか(デザインの話になってくるけど)。


ボタン

サイトを作っていく中で、outlinedv-btnをよく使うので共通化。

<template lang="pug">
v-btn(outlined large :color="color" :to="to" @click="clicked" :block="block").py-2.mt-2
  v-icon(v-if="icon" :class="iconColor + '--text'").title.mr-2 mdi-{{ icon }}
  .subtitle-1
    slot
</template>
以下略

必須ラベル

最近作ったatoms的なのでかなり気に入ってる。

<template lang="pug">
  v-chip(small label color="accent").px-2.font-weight-bold 必須
</template>

こうなる。
スクリーンショット 2019-08-29 11.17.17.png


駆け足でしたがLTなのでこれくらいにします


最後に

NoSchoolではフロントエンドが得意で、Laravel等のサーバーサイドもゴリゴリやっていきたいWebエンジニアを募集中です!(iOSエンジニアも!
回答最適化のために機械学習も取り入れていたり、勉強意欲の強い人が自由に暴れられると思いますので是非お声がけください〜。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vuetifyの気になるコンポーネントまとめ

Vuetifyのコンポーネントで良さそうなのを個人的にまとめてみた。

勉強のついでに気になるコンポーネントをまとめてみた。

Overlays

Overlays

背景マスクを簡単に作ってくれる。absolute指定でブロックに限定するのが簡単にできる。ダイアログやライトボックスなどを自前で作るときに便利

Aug-29-2019 09-27-58.gif

##App Bars

App Bars

ヘッダーくらい自分で作れと言われそうだが、めんどくさいのもまた事実。最近流行りのヘッダーラインより少し下にメニューをつける手法ができるが、応用ができなかったら結局自分で作りそう。
Collapseはなんか見慣れない不思議な動き。

Aug-29-2019 09-33-50.gif

Color pickers

Color pickers

Aug-29-2019 09-37-29.gif

Data iterators

Data iterators

例として成分表示表が使われてるけど、比較しない単体での表示が必要な場合は使いたい。filterやsearchが強力なのが嬉しい。

Aug-29-2019 09-41-47.gif

Tabs

Tabs
縦型にも対応しているし、スマホだとフリック操作でスライドもする。

Aug-29-2019 09-46-01.gif

Timelines

Timelines

略歴とかバージョンアップ履歴とかに便利なコンポーネント。
avatarとかなかなか面白い

Aug-29-2019 09-57-14.gif

Treeview

Treeview

適当にjson投げると表示を綺麗にしてくれそうなあれ。セレクトは下位の要素も同時に選択できる。

Aug-29-2019 10-01-23.gif

Steppers

Steppers

長い操作が必要な場合に画面分割&経過を視覚的に見せる。
カートとかで使われてるが、入力項目が多い場合に汎用的に使用できる。
縦型にすることもできるのでなかなか面白い。

Aug-29-2019 10-06-11.gif

まとめ

まだまだ他にも色々面白いものがありそう。使えそうだったら楽して組み込みたいなぁ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nuxt.js + GraphQL + Ruby on Railsで作ったアプリにJWT認証を追加する方法

本記事ではフロントエンドに Nuxt.js(Vue.js)、バックエンドに Ruby on Rails 、APIに GraphQL を採用したアプリケーションに、JWTトークンによる認証 を追加する方法についてまとめます。

login.gif

題材

サンプルとして以下チュートリアルで作成したToDoリストを使用します。

Nuxt.js + GraphQL + Ruby on Railsで作るToDoアプリチュートリアル(前編)
Nuxt.js + GraphQL + Ruby on Railsで作るToDoアプリチュートリアル(後編)

JWTでの認証フロー

今回実装するJWTでの認証フローを図にまとめました。

Untitled_graphql-jwt_-_Cacoo.png

トークンの発行・保存・付与・検証がめんどくさそうに見えるかもしれませんが、フロントエンド側はAuth Moduleが、バックエンド側はknockがトークンをいい感じに処理してくれるので、安心してください。

実装(バックエンド)

ユーザモデルを定義する

認証単位となるモデル(User)を生成します。

$ bundle exec rails g model user email:string password_digest:string
$ bundle exec rails db:migrate

Userはパスワードをハッシュ化して管理するので、モデルに has_secure_password を宣言します。

app/models/user.rb
class User < ApplicationRecord
  has_secure_password
end

また、Gemfileの bcrypt のコメントを外します。

Gemfile
# ・・・中略・・・

# Use Active Model has_secure_password
gem 'bcrypt', '~> 3.1.7

# ・・・中略・・・

テスト用のUserを seed.rb に追記します。

db/seeds.rb
# ・・・中略・・・

User.create(
  email: 'test@example.com',
  password: 'xxxxxxxx',
  password_confirmation: 'xxxxxxxx'
)
$ bundle exec rails db:seed

knockをインストールする

Gemfileにknockを追加し、インストールします。

Gemfile
gem 'rack-cors'
gem 'graphql'
gem 'knock' # ★追加
$ bundle install

Rails6だとknockのautoloadに失敗するのでinitializerで明示的にrequireします。

config/initializers/eager_load_knock.rb
require 'knock/version'
require 'knock/authenticable'

ジェネレータを実行します。

$ rails generate knock:install

ログイン/ログアウトのエンドポイントを準備

ジェネレータでControllerを生成します。

$ rails generate knock:token_controller user

各Controllerにて認証処理を行えるようにするため、 ApplicationController にてmoduleをincludeします。

app/controllers/application_controller.rb
class ApplicationController < ActionController::API
  include Knock::Authenticable
end

GraphqlController のbefore_actionとして認証処理を追加します。

app/controllers/graphql_controller.rb
class GraphqlController < ApplicationController
  before_action :authenticate_user

  # ・・・中略・・・
end

今回、セッションは使わないのとCORS設定済みであることを考慮して、CSRF対策を解除します。

config/application.rb
# ・・・中略・・・

module RailsNuxtGrapshqlTodoapp
  class Application < Rails::Application
    config.load_defaults 6.0
    config.api_only = true
    config.action_controller.default_protect_from_forgery = false 
  end
end

動作確認(バックエンド)

サーバ起動してInsomniaを使ってリクエストを送信してみます。

$ bundle exec rails s

未認証の状態でGraphQL Queryを送信しても、401 が返ってきます。

Insomnia__onescene__–_tasks.png

/user_token へリクエストすると、JWTトークンが返ってきます。

Insomnia__onescene__–_user_token.png

Bearerの設定でTOKEN欄に上記JWTトークンを記載します。

Insomnia__onescene__–_tasks.png

この状態で再度GraphQL Queryを送信すると、 200 が返ってきました。
JWT認証が機能していますね。

Insomnia__onescene__–_tasks.png

実装(フロントエンド)

Auth Moduleをインストールする

npmでインストールします。
Auth Moduleは store/index.js が存在していないとエラーを出すので、空のファイルを作成しておきます。

$ npm install @nuxtjs/auth @nuxtjs/axios
$ touch store/index.js

nuxt.config.jsmodules, axios, auth, apollo を追記します。

  // ・・・中略・・・
  modules: [
    '@nuxtjs/vuetify',
    '@nuxtjs/pwa',
    '@nuxtjs/eslint-module',
    '@nuxtjs/apollo',
    '@nuxtjs/axios',
    '@nuxtjs/auth'
  ],
  axios: {
    baseURL: 'http://localhost:3000/'
  },
  auth: {
    strategies: {
      local: {
        endpoints: {
          login: { url: 'user_token', method: 'post', propertyName: 'jwt' },
          user: false,
          logout: false
        }
      }
    }
  },
  // ・・・中略・・・
  apollo: {
    clientConfigs: {
      default: {
        httpEndpoint: 'http://localhost:3000/graphql',
        getAuth: () => ''
      }
    }
  }
  // ・・・中略・・・

ログイン画面を準備

rails_nuxt_grapshql_todoapp_front_-_rails_nuxt_grapshql_todoapp_front.png

/login に相当する画面およびログイン処理を実装します。

pages/login.vue
<template>
  <v-container>
    <v-row>
      <v-col cols="6" offset="3">
        <v-card>
          <v-card-title>Login</v-card-title>
          <v-card-text>
            <v-form>
              <v-container>
                <v-row>
                  <v-col cols="6">
                    <v-text-field v-model="email" label="Email" required />
                  </v-col>
                </v-row>
                <v-row>
                  <v-col cols="6">
                    <v-text-field v-model="password" type="password" label="Password" required />
                  </v-col>
                </v-row>
                <v-row>
                  <v-col>
                    <v-btn @click="login()">
                      Login
                    </v-btn>
                  </v-col>
                </v-row>
              </v-container>
            </v-form>
          </v-card-text>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  middleware({ store, redirect }) {
    window.console.log(store.state.auth.loggedIn)
    if (store.state.auth.loggedIn) {
      return redirect('/')
    }
  },
  data() {
    return {
      email: '',
      password: ''
    }
  },
  methods: {
    async login() {
      try {
        await this.$auth.loginWith('local', {
          data: {
            auth: {
              email: this.email,
              password: this.password
            }
          }
        })
        await this.$apolloHelpers.onLogin(this.$auth.getToken('local').match(/^Bearer[ ]+([^ ]+)[ ]*$/i)[1])
        this.$router.push('/')
      } catch (e) {
        window.console.log(e)
      }
    }
  }
}
</script>

無名middlewareを用いることで、既にlogin済みの状態でこのページを開くと、'/' でリダイレクトするようにしています。

methodsの login() がLOGINボタンを押したときの処理です。
Auth Moduleでログインをした後で、Apollo ModuleへJWTトークンをセットしています。

ログアウトボタンを追加

rails_nuxt_grapshql_todoapp_front_-_rails_nuxt_grapshql_todoapp_front.png

ログイン中の場合のみ、メニューバーにログアウトボタンを表示します。

layouts/default.vue
<template>
  <v-app>
    <v-app-bar app>
      <v-toolbar-title v-text="title" />
      <div class="flex-grow-1" />
      <span v-if="loggedIn" @click="logout()">Logout</span>
    </v-app-bar>
    <v-content>
      <nuxt />
    </v-content>
    <v-footer center>
      <v-layout justify-center>
        <span>&copy; 2019 Yuhei Okazaki. All Rights Reserved.</span>
      </v-layout>
    </v-footer>
  </v-app>
</template>

<script>
export default {
  data() {
    return {
      title: 'Tasks'
    }
  },
  computed: {
    loggedIn() {
      return this.$auth.loggedIn
    }
  },
  methods: {
    async logout() {
      try {
        await this.$auth.logout()
        await this.$apolloHelpers.onLogout()
        this.$router.push('/login')
      } catch (e) {
        window.console.log(e)
      }
    }
  }
}
</script>

methodsの logout() がLOGOUTを押したときの処理です。
Auth Moduleでログアウトをした後で、Apollo Moduleもログアウトしています。

未認証時のリダイレクトを追加

このままだと、ログインしていない状態でもタスク一覧画面を開けてしまうので、ログインしていないときには /login へ飛ばすようmiddlewareを設定します。

pages/index.vue
// 中略
export default {
  middleware: 'auth'
  // 中略
}

動作確認(全体)

冒頭の画像のように、ログインしたときのみタスク一覧が表示されます。

login.gif

まとめ

本記事ではバックエンドにRuby on Rails、フロントエンドにNuxt.js、APIにGraphQLを採用したアプリケーションにJWT認証を追加しました。
新規登録画面やタスクとユーザの紐付け等、未実装の処理は多数あるものの、認証というアプリケーションを実装するときの最初の壁は越えられたかと思います。

knockやAuth Module、Apollo Moduleを用いたことで、トークン操作を意識せず簡単に認証追加できたので、ぜひお試しください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む