- 投稿日:2020-03-17T23:50:55+09:00
Laravel7.XでVue.jsを実装してみる
npmを起動してみる
npm run watchを叩くと以下のエラーメッセージが出力
terminal'cross-env' is not recognized as an internal or external command, operable program or batch file.こちらのリンクを参考にして解決
https://stackoverflow.com/questions/45034581/laravel-5-4-cross-env-is-not-recognized-as-an-internal-or-external-commandBootstrap-Vueの実装
こちらのドキュメントを参考に実施(https://bootstrap-vue.js.org/docs)
1) npmでbootstrap-vueをインストール
terminal# With npm npm install vue bootstrap-vue bootstrap2) bootstrap-vueをインポート
resources\js\bootstrap.jsimport Vue from 'vue' import { BootstrapVue, IconsPlugin } from 'bootstrap-vue' import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap-vue/dist/bootstrap-vue.css'3) BladeとVueファイルをVueに合わせて構成
(作成中…)
Laravel5.xではVue環境が構築されていた気がするが(Example-component等)
7.xはない…
- 投稿日:2020-03-17T21:50:48+09:00
コロナウイルスから学ぶdocker-compose
はじめに
最早今更感ありますが、新型コロナウイルス対策サイトが話題ですね
オープンソースコミュニティへの貢献の方法については既に非常に丁寧な記事が出ているので割愛しますが、
docker-composeの紹介をしている人が少ないので書いてみます勿論ボランティアで何か貢献できれば何よりですが、
このプロジェクトはコミットしないとしてもdocker-composeやvue.jsに触れてみるのにかなりお勧めです
正直お国が主導のプロジェクトがどんな地獄なのか見学するつもりで覗いてみたのでびっくりしたdocker-compose使ってみよう
新型コロナウイルス対策サイトのgitリポジトリ
ソースコードをクローンしたら、出来上がったディレクトリ上でdocker-compose upブラウザでlocalhost:3000にアクセスすると動くようになります、すごーい
以上、みんなも試しにやってみてね
解説
当然docker及びdocker-composeはインストールする必要がありますが...
コマンドいくつか叩けばインストールできるので割愛docker-composeで動くことを確認するためにどこを見るのか
プロジェクトのルートディレクトリにDockerfileが
FROM node:10.19-alpine WORKDIR /app COPY package.json yarn.lock ./ RUN yarn install COPY . . EXPOSE 3000 ENV HOST 0.0.0.0 CMD ["yarn", "dev"]そしてdocker-compose.ymlがあります
version: "3" services: app: container_name: covid19 build: context: . dockerfile: Dockerfile tty: true ports: - 3000:3000 volumes: - .:/app - node_modules:/app/node_modules volumes: node_modules: {}docker-compose.ymlがあるからには環境構築がdocker-composeで行えるはずです。
内容によってはこれらの中身を見ればどのようなフレームワークなのかもおおよそ推測できるでしょう
全てを解説すると長くなるし、そもそも完璧に説明できるほど熟知しているかというと微妙ですがこのファイルを見ればdocker-compose.ymlの記述に従って、
covid19
という命名のappコンテナが立ち上がり、
localhostの3000番ポートでアクセスできるようになる、
という動きをまずは雰囲気で感じることができれば良いと思います終わりに
遊んでみたくなるdocker-composeコマンドを紹介
docker-compose up 通常の起動、終了する時はctrl + c docker-compose up -d => dオプションでデーモン化、バックグラウンド起動できる docker-compose exec app bash => 起動中のコンテナの中に入ることができる docker-compose down => docker-composeで管理しているコンテナを全部終了する
- 投稿日:2020-03-17T21:47:23+09:00
オンラインSwiftコード生成ツールをつくった
概要
ブラウザで利用できるSwiftコード生成ツールを作成しました。
https://shtnkgm.github.io/SwiftCodeGenerator/↓こんなの
SwiftでTDDを進めるにあたり、Mockとなるインスタンスの生成コードを書くのが大変だっため、Webツールを作成しました。
SourceryやPure Swiftでやる方法も検討しましたが、Vue.jsでコードも書きたかったため、Web技術で作成しました。できること
以下のような型定義を入力フォームにコピペすると、
struct Book { let price: Int let title: String }以下のようなコードを出力します。
メンバーワイズイニシャライザ
extension Book { init( price: Int, title: String ) { self.price = price self.title = title } }ファクトリメソッド(適当な値でインスタンスを生成するmakeメソッド)
extension Book { static func make( price: Int = 0, title: String = "" ) -> Book { return Book( price: price, title: title ) } }Codableに準拠するための実装
extension Book: Codable { enum CodingKeys: String, CodingKey { case price case title } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) price = try container.decode(Int.self, forKey: .price) title = try container.decode(String.self, forKey: .title) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(price, forKey: .price) try container.encode(title, forKey: .title) } }Equatableに準拠するための実装
extension Book: Equatable { static func == (lhs: Book, rhs: Book) -> Bool { return lhs.price == rhs.price && lhs.title == rhs.title } }構成
コード生成処理はクライアントサイドで行うため、かなりライトな構成です。
- JSフレームワーク: Vue.js
- CSSフレームワーク: UIkit JS
- ホスティング: GitHub Pages
試してみてください
β版で不具合や足りない点もあるかと思いますが、活用いただけたら幸いです。
Swift Code Generator
https://shtnkgm.github.io/SwiftCodeGenerator/
- 投稿日:2020-03-17T18:54:55+09:00
地図を自由自在に描画できたらワクワクした話 ~ 新型コロナウィルスの感染情報を可視化したサイトを Firebase Hosting で公開
新型コロナウィルスの感染情報を可視化したサイトを作ったのですが、その際に地図を自由自在に描画できてワクワクした気持ちを共有できればと思います。
新型コロナウィルスの感染情報を可視化したサイト
https://hazard.westa.io/もしよかったらご覧ください。
1. 地図の描画に D3.js を使う
地図を自由に描画できたら、色々可能性の幅が広がりそうでワクワクしませんか?
今回は『D3.js を使用すると地図を自由に描画できるよ!』って紹介記事です。1.1 地図データの準備
(1) Natural Earth から世界地図のデータを取得
- 取得した地形データのファイル拡張子は
.shp
- なお、この地図データには日本の都道府県情報も入っている (後述する geojson 形式に変換したのちにブラウザ上で動的に都道府県ごとに色を変えたりといった事も可能です)
(2) QGIS ソフトで地図を加工・ファイル形式変換
- フリーソフト。地形のデータとなる点を移動したり、不要な地形を削除したり加工ができる
.shp
から geojson 形式に変換できる(3) mapshaper でデータを軽量化
- 地形のデータとなる点情報をイイ感じに削減してデータを軽量化できる
1.2 地図データの描画
- 上記で用意した地図データを D3.js を使用して SVG として描画
↑ D3.js を使い日本地図を描画して、感染情報を元に色を付けた。QGIS を使い沖縄県の位置を上部に移動できたが、沖縄と本土を区切る線を入れたかったが方法がわからずこのまま
↑ D3.js を使い世界地図を描画して、感染情報を元に色を付けた。地図データを軽量化しすぎてロシア上部、カナダ上部、グリーンランドなどが異様なほどチープな形状に... 言わなきゃ気付かんか、あははー(*'▽')実際のコードは以下の記事が参考になりました。
noguchi さんの記事
d3で地図を描画する2. D3 以外の地図描画方法
D3.js を使用しないでもっとお手軽に地図を表示して JavaScript から制御する方法はありますが今回採用は見送りました。
2.1 Japan Map - jQuery plugin
D3.js で実現する方法と比べて、とってもお手軽で導入コストが低そうです。
ですが、このライブラリは jQuery が必要であり、私は Nuxt.js を使いたい都合上、共存が面倒そうで利用しませんでした。2.2 Google Charts / GeoChart
当初はこれで実装していましたが、GCP の Google Maps API を経由(mapsApiKeyが必要)するため課金対象となるようでした(間違っていたらごめんなさい)。
3. その他 利用技術
ついでに今回作成したサイトの技術情報を紹介させてください!
これまで色々なライブラリで遊んできましたが、ここ最近は私の中でTypeScript
,Nuxt.js (Vue.js)
,Vuetify
,firebase
に落ち着いた感があります。(1) Nuxt.js @2.11 (Vue.js のフレームワーク)
- お手軽に SPA/SSR サイトを立ち上げるのに Vue.js のフレームワークである Nuxt.js を選定
- なお、今回は SSR(サーバー・サイド・レンダリング) 不要のため SPA(シングル・ページ・アプリケーション) とした
- PWA 対応もお手軽
(2) Vuetify @2.2 (デザイン・コンポーネントフレームワーク)
- お手軽で信頼性のある UI ライブラリとして Vuetify を選定
- スクロールなどにより、とある要素が表示されるタイミングで遅延実行が行える v-intersect 機能など、素晴らしい機能が盛りだくさん
- スマホのネイティブアプリっぽい雰囲気にできる
↑ 最近のスマホアプリは画面下にタブが固定表示されるものが多くなってきたが、Vuetify を使うとお手軽に実現できる(3) vue/composition-api @0.4 (Vue.js v2 系で v3 系の機能を使えるライブラリ)
現状の vue の最新安定版は v2 系ですが、v3 系がアルファ段階で公開されています。
v3 のアルファ版はまだまだ挑戦する勇気が持てなかったので、代わりに v2 系で v3 系の composition-api が試せるライブラリを使ってみました。TypeScript とも相性が良く結構イイ感じです。使い方は以下の記事が参考になりました。
@ryo2132 さんの記事
先取りVue 3.x !! Composition API を試してみる(4) Chart.js @2.9
- 可視化するうえでチャート (棒グラフや折れ線グラフ、円グラフなど) をお手軽に描画するためのライブラリ
- とってもお手軽にきれいなチャートを描画できる
チャートライブラリの選定には以下の記事が役に立ちました。
@awakia さんの記事
チャート、グラフを書くのに良さそうなJavascriptライブラリ6選(5) PWA
- Web サイトを直接ホームに追加することでアプリケーション化できます。(以下の『感染情報』アプリがそれです)
- PWA にはアプリ化するにあたって色々な機能がありますが、以下の記事がとっても詳しいです。
@umamichi さんの記事
PWA 入門 〜iOS SafariでPWAを体験するまで〜 2019年7月更新(6) TypeScript @3.8
- TypeScript Love!
4. 現状の運用費用
- Google/Firebase-Hosting
- 今のところ無料枠内
- ドメイン代
- もともと持っていたドメインからサブドメインを作成したため追加費用なし
参考までに記載しておきます。
勢いで書いてしまったので変な部分も多分にありそうですが、皆さんも
D3.js + 地図描画
いかがでしょうか。
記事の内容が『役に立った』『面白かった』と思ったら、ぜひ LGTM(いいね) を頂けると嬉しい限りでございます!
- 投稿日:2020-03-17T18:34:56+09:00
シニアエンジニアに聞いたvue関連ライブラリの選定方法
同僚の @kubotak さんに聞いたライブラリ選定基準をまとめます。今回は特にフロントエンド領域でvueに絞って書きます。
1. 公式がまとめているライブラリリストをみる
まずはともあれ、vuejsが公式でまとめているライブラリリストを参照する。
https://github.com/vuejs/awesome-vue
ここにやりたいことにマッチしそうなライブラリを全部一通り見てみる。
2. star数を確認する
あまりにも使っている人が少ないと、コミュニティがないことが想定される
3. 新規 issue を確認して、それがPRに反映されているか
issue が無いのはユーザが少ないか、すでに枯れている(安定してバグなども少ない)ライブラリ。
issue が多いのにPRが作られていない、またはマージされていないライブラリはメンテナが居なくなっているので避けるのが吉。
4. 最終リリースタグとnpmのページを確認する
githubでreleasesをみると最後にいつリリースされているかわかる。ただしnpmの場合にはrelease tagが切られていなくてもnpm packageとしてリリースされていることがあるのでそちらも確認する。
あまりに古いのだと新しいvueバージョンに追従できない可能性などを考慮した方が良い。5. readme がきちんと書かれているか
有用な情報が書かれていないと導入は辛くなります。
6. 動作例が書かれているか
あれば何ができるかよりイメージがつきやすくなります。無い場合には自分で動かさないといけないのでややハードルは上がる。
7. ファイルサイズは大きすぎないか
あまりに便利すぎるライブラリは全部入りでファイルサイズが大きくなりがちです。
nuxtなどフレームワークで適切に配信されるものが分割される場合にはそこまでシビアにならなくても良い。8. 2個くらいまで絞り込めたら実際に使ってみる
何個かまで絞れて迷うところまできたら、導入しやすそうか使いやすそうかを実際に使ってみて確かめる。
- 投稿日:2020-03-17T17:29:12+09:00
Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part4
前回のパート
前回は、全ページで使用するヘッダーの作成と会員登録の処理の実装を行いました
前回のパートはこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part3
今回は、前回実装し登録をした内容を利用してログイン機能の実装をしていこうと思いますログインフォームの実装(Vue側)
まずはログインフォームのview側の実装をしていきます
client/components
にLogin.vue
を作成して下記を追加してくださいLogin.vue<template> <div> <Header /> <div class="main-container"> <v-form v-model="valid"> <v-text-field v-model="form.email" :rules="emailRules" label="E-mail" required></v-text-field> <v-text-field v-model="form.password" :rules="passwordRules" label="Password" required type="password"></v-text-field> <v-btn small>submit</v-btn> </v-form> </div> </div> </template> <script> import Header from './Header'; export default { components: { Header }, metaInfo: { title: 'ログイン', htmlAttrs: { lang: 'ja' } }, created () { const user = this.$store.getters['auth/user']; if (user !== null) { this.$router.push('/todo'); } }, data () { return { form: { email: '', password: '', }, emailRules: [ v => !!v || 'E-mailを入力してください', v => /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) || 'E-mailを正しく入力してください' ], passwordRules: [ v => !!v || 'パスワードを入力してください' ] } } } </script> <style> .main-container { width: 500px; margin: auto; } </style>前回の
Register.vue
と内容はほとんど同じなので
特に説明は必要ないかと思いますので、割愛します
説明が必要な方は、前回のパートに戻って確認してみてくださいではここにmethodを追加していきます
Register.vue<template> <div> <Header /> <div class="main-container"> <v-form v-model="valid"> <v-text-field v-model="form.email" :rules="emailRules" label="E-mail" required></v-text-field> <v-text-field v-model="form.password" :rules="passwordRules" label="Password" required type="password"></v-text-field> <v-btn small @click="login">submit</v-btn> //追加 </v-form> </div> </div> </template> <script> import Header from './Header'; export default { components: { Header }, metaInfo: { title: 'ログイン', htmlAttrs: { lang: 'ja' } }, created () { const user = this.$store.getters['auth/user']; if (user !== null) { this.$router.push('/todo'); } }, data () { return { form: { email: '', password: '', }, emailRules: [ v => !!v || 'E-mailを入力してください', v => /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) || 'E-mailを正しく入力してください' ], passwordRules: [ v => !!v || 'パスワードを入力してください' ] } }, methods: { //追加 async login() { await this.$store.dispatch('auth/login', this.form); this.$router.push('/todo'); } } } </script> <style> .main-container { width: 500px; margin: auto; } </style>ログイン時のmethodを追加しました
内容は、auth.js
のlogin
アクションにフォームに入力された内容を渡しdipatchしています
その処理が終われば、/todo
のTodoページに遷移する様に設定してますでは次に、
auth.js
のlogin
アクションを定義していきます
'auth.js`に下記を追加してくださいauth.jsimport axios from 'axios'; const state = { user: null } const getters = { user: state => state.user } const mutations = { setUser (state, user) { state.user = user; } } const actions = { async register({ commit }, data) { const response = await axios.post('/api/register', data) commit('setUser', response.data); }, async searchUser({ commit }) { const response = await axios.get('/api/user'); const user = response.data ? response.data : null; commit('setUser', user); }, async login({ commit }, data) { //追加 const response = await axios.post('/api/login', data); commit('setUser', response.data); } } export default { namespaced: true, state, getters, mutations, actions }今回追加したアクションはLaravelで定義した、
/login
のルートに対して入力されたデータをPOSTし
その、情報を変数responseに格納しています
そして、その値をsetUser
mutationに対してcommitしstateのuserの値をセットしています
registerの時と同じ流れになります次に、Laravel側の実装をしていきます
Laravel側の実装
Vue側での
auth.js
でPOSTしていた/login
をルートに定義していきます
api.php
に下記を追加してくださいapi.phpRoute::post('/register', 'Auth\RegisterController@register')->name('register'); Route::post('/login', 'Auth\LoginController@login')->name('login'); //追加 Route::get('/user', function(){ return Auth::user(); });これで
auth.js
のlogin
アクションからaxiosでリクエストを投げられる様になりましたそして、part3で下記の記述を
LoginController.php
に追加してますのでこれでLaravel側での実装は完了ですLoginController.phpprotected function authenticated(Request $request, $user) //追加 { return $user; }上記までの記述で一通りログイン機能の実装が終わりました
ただ遷移する画面の/todo
をまだ用意していないので正常に画面が遷移することができないので
ここで、Todo.vue
コンポーネントを追加しておきましょう
client/components
にTodo.vue
ファイルを新規で作成してください
中身はこの様にしておいてくださいTodo.vue<template></template> <script></script> <style></style>次に
router.js
にコンポーネントを登録しておきます
router.js
に下記を追加してくださいrouter.jsimport Vue from 'vue'; import Router from 'vue-router'; import Login from '../components/Login'; import Register from '../components/Register'; import Todo from '../components/Todo'; //追加 Vue.use(Router); export default new Router({ mode: 'history', routes: [ { path: '/login', component: Login }, { path: '/signup', component: Register }, { path: '/todo', //追加 component: Todo } ] })ここまでできたらログインを試してみてください
正常であれば、ログイン後/todo
に遷移し真っ白な画面が表示されるかと思います終わりに
今回はログイン機能の実装を行いました
それに加えて、/todo
ページの用意も行いました次回は、ログアウト機能を実装したいと思います
次回のログアウト機能が終われば認証周りの実装は終わりですので、Todoを作っていく実装に入っていきます次回のパートはこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part5
- 投稿日:2020-03-17T17:28:37+09:00
Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part3
前回のパート
前回は、Authの導入とマイグレーションファイルを作成し
今回のTodoアプリで使用するテーブルの作成まで行いました
前回のパートはこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part2
今回は、全てのページで使うヘッダーの作成と認証周りの実装をやっていきますヘッダーコンポーネントの作成
全てのページで共通で使うヘッダーを作成していきます
Vuetifyで実装しますので、Vuetifyを使用しない方は自作での実装をしてください
まず、client/components
にHeader.vue
を作成し下記を追加してくださいHeader.vue<template> <v-card color="grey lighten-4" flat height="200px" tile> <v-toolbar dense> <v-app-bar-nav-icon></v-app-bar-nav-icon> <v-toolbar-title>Todo-List</v-toolbar-title> <v-spacer></v-spacer> <router-link to="/signup"> <v-btn icon> <v-icon>mdi-account</v-icon> </v-btn> </router-link> <router-link to="/todo"> <v-btn icon> <v-icon>mdi-align-horizontal-left</v-icon> </v-btn> </router-link> <router-link to="/login"> <v-btn icon> <v-icon>mdi-login</v-icon> </v-btn> </router-link> <v-btn icon> <v-icon>mdi-logout</v-icon> </v-btn> </v-toolbar> </v-card> </template>templateのみのシンプルな物です
各<v-btn>
を<router-link>
で囲み、会員登録やログインやログアウト、Todoページのリンクを設定しています
全てVuetifyになります
詳しく知りたい方はこちらを参照ください→こちら
これでヘッダーの用意は終わりです会員登録の実装
まずはVue側でフォームの実装をしていきます
client/components
にRegister.vue
を作成してください
Register.vue
にを下記の様に下記を追加してくださいRegister.vue<template> <div> <Header /> <div class="main-container"> <v-form v-model="valid"> //・・・① <v-text-field v-model="form.name" :rules="nameRules" label="Name" required></v-text-field> //・・・② <v-text-field v-model="form.email" :rules="emailRules" label="E-mail" required></v-text-field> <v-text-field v-model="form.password" :rules="passwordRules" label="Password" required type="password"></v-text-field> <v-text-field v-model="form.password_confirmation" :rules="repasswordRules" label="Confirm Password" required type="password"></v-text-field> <v-btn small>submit</v-btn> </v-form> </div> </div> </template> <script> import Header from './Header'; export default { components: { Header }, metaInfo: { //・・・③ title: '会員登録', htmlAttrs: { lang: 'ja' } }, data () { return { form : { name: '', email: '', password: '', password_confirmation: '', }, nameRules: [ v => !!v || '名前を入力してください', //・・・④ ], emailRules: [ v => !!v || 'E-mailを入力してください', v => /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) || 'E-mailを正しく入力してください' //・・・⑤ ], passwordRules: [ v => !!v || 'パスワードを入力してください', v => v.length >= 8 || 'パスワードは8文字以上で入力してください' //・・・⑥ ], repasswordRules: [ v => !!v || 'パスワードの再入力を行ってください', v => v == this.form.password || 'パスワードが一致しません' //・・・⑦ ] } } } </script> <style> .main-container { width: 500px; margin: auto; } </style>①ここは全てVuetifyになります
ここでは簡単な説明はしますが、もっと詳しく知りたい方は、こちらをご覧ください→こちら②Vuetifyのフォームになります
:rules
この部分で下の④⑤⑥⑦のルールとバインディングしており、フロント側でのバリデーションを行っています
require
は入力必須を意味します
label
でフォームのラベル名に設定できます
②で設定しているものはどれも同じ要領です
次に設定しているバリデーションのルールについて軽く触れておきます
④入力の値が存在しているかどうか(未入力でないか)をチェックしています
⑤正規表現でEmailの形式かどうかをチェックしています
⑥パスワードが8文字以上で入力されているかをチェックしています
⑦passwordの入力値を一致しているかをチェックしています③part1でインストールしたVue Metaです
ここでは、titleを【会員登録】としてlangをjaに設定しています
Vue Metaをこの様な設定を行うために使用しますこれで、Vuetifyを使って簡単にフォームを用意できバリデーションまで行えました
次に会員登録機能の実装を行っていきます会員登録機能の実装(Laravel側)
まずは、
routes/api.php
を開きルートを定義していきます
/register
は会員登録用のルート
/user
はログインをしているのかを常時チェックするために用意したルートになります(この後のVue側の実装で再度説明します)api.php<?php use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; Route::post('/register', 'Auth\RegisterController@register')->name('register'); //追加 Route::get('/user', function(){ //追加 return Auth::user(); });次に
app/Http/Controllers/Auth/RegisterCOntroller.php
を開いて下記を追加しますRegisterController.phpprotected function registered(Request $request, $user) //追加 { return $user; }
registerd()
メソッドをオーバーライドすることで
登録処理をした際にUser情報を返却できる様に設定できます
この値を使って今後認証を行うので、上記を追加しましたこのついでに
ログイン時にとログアウト時にも必要になる処理を先にやっておきます
app/Http/Controllers/Auth/LoginController.php
を開いて下記を追加してくださいLoginController.phpprotected function authenticated(Request $request, $user) //追加 { return $user; }こちらの追加の内容も、
registerd()
メソッドのオーバーライドをした際と同じ理由になり
ログイン時にUserの情報を返却する様に設定しています次に
LoginController.php
でさらにloggedOut()
メソッドを追加してくださいLoginController.phpprotected function loggedOut(Request $request) { $request->session()->regenerate(); return response('', 200); }こちらは、ログアウト時に呼ばれるメソッドになり
ログアウト時にセッションを再生成を行い、レスポンスを返す様にしてあります上記3つの追加点については、本記事作成でも参考にさせて頂いたこちらの記事を参照してください→こちら
以上で会員登録に必要なLaravel側の処理の実装は終わりです(ログイン時とログアウト時の設定も行いました)
会員登録処理(Vue側)
まず
Register.vue
に下記を追加してくださいRegister.vue<template> <div> <Header /> <div class="main-container"> <v-form v-model="valid"> <v-text-field v-model="form.name" :rules="nameRules" :counter="10" label="Name" required></v-text-field> <v-text-field v-model="form.email" :rules="emailRules" label="E-mail" required></v-text-field> <v-text-field v-model="form.password" :rules="passwordRules" label="Password" required type="password"></v-text-field> <v-text-field v-model="form.password_confirmation" :rules="repasswordRules" label="Confirm Password" required type="password"></v-text-field> <v-btn small @click="register">submit</v-btn> //追加 </v-form> </div> </div> </template> <script> import Header from './Header'; export default { components: { Header }, metaInfo: { title: '会員登録', htmlAttrs: { lang: 'ja' } }, created () { const user = this.$store.getters['auth/user']; if (user !== null) { this.$router.push('/todo'); } }, data () { return { form : { name: '', email: '', password: '', password_confirmation: '', }, nameRules: [ v => !!v || '名前を入力してください', ], emailRules: [ v => !!v || 'E-mailを入力してください', v => /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) || 'E-mailを正しく入力してください' ], passwordRules: [ v => !!v || 'パスワードを入力してください', v => v.length >= 8 || 'パスワードは8文字以上で入力してください' ], repasswordRules: [ v => !!v || 'パスワードの再入力を行ってください', v => v == this.form.password || 'パスワードが一致しません' ] } }, methods: { //追加 async register() { await this.$store.dispatch('auth/register', this.form); this.$router.push('/todo'); } } } </script> <style> .main-container { width: 500px; margin: auto; } </style>①クリックイベントを追加しています
クリックすることでregister
メソッドを呼び出します②このメソッドによって登録処理を実行します
厳密に言うと、登録処理をするアクションを呼び出しています(auth.jsについてはこの後実装します)
会員登録の処理後に、Todoページに遷移する様にしています次に、
register()
でdispatchしているauth.js
のregister
の実装をしていきますauth.jsimport axios from 'axios'; const state = { user: null //追加 } const getters = { user: state => state.user //追加 } const mutations = { setUser (state, user) { //追加 state.user = user; } } const actions = { async register({ commit }, data) { //追加 const response = await axios.post('/api/register', data) commit('setUser', response.data); }, async searchUser({ commit }) { //追加 const response = await axios.get('/api/user'); const user = response.data ? response.data : null commit('setUser', user); }, } export default { namespaced: true, state, getters, mutations, actions }main.jsimport Vue from 'vue'; import App from './App.vue'; import router from './router/router'; import store from './store/store'; import vuetify from './plugins/vuetify'; import axios from 'axios'; import VueMeta from 'vue-meta'; Vue.config.productionTip = false Vue.use(axios); Vue.use(VueMeta, { refreshOnceOnNavigation: true }); const createApp = async () => { //追加 await store.dispatch('auth/searchUser'); new Vue({ router, vuetify, store, render: h => h(App) }).$mount('#app') } createApp(); //追加一気にauth.jsの追加とmain.jsの一部追加を行いました
一つづ説明していきますauth.jsconst state = { user: null }stateでuserの情報を管理します
このアプリケーション内では、全てこのuserの値の有無を見てログイン認証やユーザー情報の所得を行いますauth.jsconst getters = { user: state => state.user }stateのuserの情報を所得するgetterを定義しています
auth.jsconst mutations = { setUser (state, user) { state.user = user; } }受け取ったデータをstateのuserに値をセットするミューテーションを定義しています
auth.jsasync register({ commit }, data) { //追加 const response = await axios.post('/api/register', data) commit('setUser', response.data); }Laravel側の
api.php
で定義したregister
に対してpostしています
第二引数のdataはフォームに入力された値がv-modelでバインドされたdataの中身が渡されています
ここでの返り値として、先ほどオーバーライドしたregistered()
メソッドでの$userの値がresponseにセットされます
その値をsetUserに対してcommitしてstateのuserに値を設定していますauth.jsasync searchtUser({ commit }) { //追加 const response = await axios.get('/api/user'); const user = response.data ? response.data : null; commit('setUser', user); }Laravel側の実装の際に
api.php
にて定義した/user
のルートに対してアクセスをしています
/user
のルートはAuth::user()として認証しているユーザーの情報を返却する様に作りました
Auth::user()は認証していればユーザー情報が、認証されていなければnullが返ってきます
それを利用して、上記アクションでは/api/user
に対してリクエストをしてその戻り値(認証されていればユーザー情報、認証されていなければnull)を
変数responseに入れています
const user = response.data ? response.data : null;
で認証されていてresponseに値が入っていればその値を
認証されておらずnullが入っていればnullを変数userに入れています
そのuserをsetUserでcommitしてstateのuserに値を入れています
なのでstateのuserはログインしていればユーザー情報、ログインしていなければnullが常に入っている様にしています
あとはこれを常時実行するためにどうしているかと言うと、先ほど追加したmain.js
の追加部分になりますmain.jsconst createApp = async () => { await store.dispatch('auth/searchUser'); new Vue({ router, vuetify, store, render: h => h(App) }).$mount('#app') } createApp();ここでVueインスタンスが作られる際に毎回、
createApp()
を実行することにより
auth.js
のsearchUser
アクションを実行しています
これにより、auth.js
のsearchUser
が毎回実行されることで
Laravelで定義した/user
にリクエストを投げ現在の認証情報を所得しauth.js
のsetUser
がcommitされ
stateのuserに値がセットされると言うことになりますRegister.vuecreated () { const user = this.$store.getters['auth/user']; if (user !== null) { this.$router.push('/todo'); } }この部分でそのページにアクセスする度に
created()
が実行され
auth.js
のgettersを利用してstateの現在のuser情報を所得し
ログインしていれば/todo
のページへ遷移する様に設定しています(ログインしていない場合はログインページが表示される)ここまでの実装で
会員登録が行える様になりますので、フォームから入力して登録を実行してみてください
正常であれば、入力内容がDBに保存され登録処理ができます終わりに
今回はヘッダーのコンポーネントの作成と、会員登録機能の実装を一通り行いました
次回は、今回登録した内容を利用してログインができる様に、ログイン機能の実装をやっていきます次のパートはこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part4
- 投稿日:2020-03-17T17:27:52+09:00
Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part2
前回のパート
前回はVue CLIとLaravelの環境構築を含め、VuexやVue RouterのセットアップとVue MetaとVuetifyの
インストールを行いました
前回の記事はこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part1
今回は、本記事で作成するTodoリストに使うテーブル作成から行いますAuthの導入
まずはターミナルから
$ php artisan serve
と$ npm run serve
を実行して
LaravelとVue CLIを動かしておいてくださいまた事前に、.envファイルのDBの設定とDBの作成を行ってください
Laravelの認証を使うためにAuthを導入していきます
コマンドにて下記を実行してください
$ composer require laravel/ui
$ php artisan ui vue --auth
$ php artisan migrate
$ npm install
$ npm run dev
上記を順に実行することで、LaravelでAuthが導入できLaravel側でのデフォルトの認証画面にアクセスできる状態になります(今回はview側はVueを使用するため使いません)マイグレーションの作成
まず今回使うテーブルは2種類になります
ユーザーの情報を扱う、UsersテーブルとTodoの情報を管理するTodosテーブルになります
Usersテーブルは上記のAuthの導入をした際に、作成されているUsersテーブルをそのまま使うので
ここで作るテーブルはTodosテーブルのみになります
早速Todosテーブルのマイグレーションを作成していきます
コマンドにて下記を実行
$ php artisan make:migration create_todos_table --create=todos
作成されたファイル
database/migrations/XXXX_XX_XX_XXXXXXX_create_todos_table.php
を開き下記と同じ様に書き換えてくださいXXXX_XX_XX_XXXXXXXX_create_todos_table.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class TodosCreateTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('todos', function (Blueprint $table) { $table->id(); $table->integer('user_id'); $table->text('text'); $table->softDeletes(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('todos'); } }上記が完了したら、マイグレーションを実行しましょう
$ php artisan migrate
実行したら作成したマイグレーションのテーブルが作成できているか、確認してください以上で今回使用する、テーブルの準備が整いました
終わりに
今回は、Authの導入とマイグレーションファイルの作成を行いました
次回から会員登録やログインなどの認証まわりの実装に入っていきます次のパートはこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part3
- 投稿日:2020-03-17T17:27:02+09:00
Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part1
はじめに
本記事は、Vue CLIとLaravelに加えてVuetifyを使ってTodoリストを作るチュートリアルです
Vuetifyは各自採用するかは決めていただければと思います。
本記事にはVuetifyの記載が多く登場しますが、Vuetifyを使わずにこのチュートリアルを進められる方はVuetifyの部分は無視してもらい
自身でマークアップを行ってください。
また、本チュートリアルはこちらの記事を参考にさせて頂き制作しましたこのチュートリアルで学べること
- Vue CLIとLaravelを利用した開発の流れ
- Vue(SPA)での開発
- LaravelとVueでの認証周りのあれこれ
- Vuexの活用方法
- Vue Routerの活用方法
このチュートリアルの対象者
- Laravelの基本を学んだ方
- Vueの基本を学んだ方
- Vue CLIとLaravelで何か作ってみたい方
バージョン
Vue CLI - 4.0.5
laravel - 7.0.4Vue CLI × Laravelの環境構築
まずはLaravelでアプリケーションを作成していきます
コマンドにて下記を実行
$ laravel new VueTodo
作ったアプリケーションに移動します
$ cd VueTodo
次にVue CLIをLaravelに作っていきます
$ vue create client
Manualyを選択
RouterとVuexを選択
historyモードを使用するのでyを押してEnter
今回はエラー防止のみのESLintを使用するのでそのままEnter
保存時にLintを実行するため、そのままEnter
package.jsonで管理するため、Inpackage.json
を選択してEnter
今回の設定を今回は特に保存しないので、nを押してEnter
これでLaravelにclientと言う名前でVue CLIが構築されます
ここまでで、LaravelとVue CLIの準備は完了proxyの設定を行う
Vue CLIとLaravel間ではドメインが違うため、お互いに通信を行うための設定が必要です
今回はproxyを使います
Vue CLIの機能を使いproxyの設定をすることができます。clientディレクトリに
vue.config.js
ファイルを作成
下記を追加
これによりVue CLIからの通信は http://127.0.0.1:8000 に変換され同一ドメインでの通信が可能になります
(ここのパスは環境によって変わるため、$ php artisan serve
をした際にアクセスできるLaravel側の自身の環境に書き換えてください)vue.config.jsmodule.exports = { devServer: { proxy: { '/api': { target: 'http://127.0.0.1:8000' } } } }RouteServiceProviderの変更
app/Http/Providers/RouteServiceProvider
のmapApiRoutes()
メソッドの変更を行います
デフォルトでは下記のようになっていますRouteServiceProvider.phpprotected function mapApiRoutes() { Route::prefix('api') ->middleware('api') //・・・① ->namespace($this->namespace) ->group(base_path('routes/api.php')); }①の部分がデフォルトではapiとなっています
このmiddlewareの設定を下記のように書き換えますRouteServiceProider.phpprotected function mapApiRoutes() { Route::prefix('api') ->middleware('web') ->namespace($this->namespace) ->group(base_path('routes/api.php')); }何故ここを書き換えるのかと言うと
apiとwebでは適応されるmiddlewareが違います
apiのmiddlewareはCSFRトークンやセッションなどのmiddlewareが含まれていません
ただ今回の場合は、Laravelのセッションの認証をするためにセッションや、クッキーを必要とするため
webに変更しています
従来のAPIとしてLaravelを利用する場合は、apiのデフォルトのままで大丈夫ですaxiosのインストール
コマンドにて下記を実行
$ npm install axios
clietn/src/main.js
にてaxiosをuseするmain.jsimport Vue from 'vue'; import App from './App.vue'; import axios from 'axios'; Vue.config.productionTip = false Vue.use(axios); new Vue({ render: h => h(App) }).$mount('#app')Vue Routeのセットアップ
Vue Routerセットアップを行います
すでにVue CLIの作成時にRouterを導入してるのでインストールは不要です
client/src/router
と言うディレクトリがRouterのファイルになります
client/src/index.js
のファイル名をわかりやすい様にリネームし、router.js
と変更し
router.js
に下記に書き換えてくださいrouter.jsimport Vue from 'vue'; import Router from 'vue-router'; Vue.use(Router); export default new Router({ mode: 'history', routes: [ //今は空にしておく ] })
router.js
の作成が完了したら、main.js
にて下記を追加main.jsimport Vue from 'vue'; import App from './App.vue'; import router from './router/router'; //追加 import axios from 'axios'; Vue.use(axios); new Vue({ router, //追加 render: h => h(App) }).$mount('#app')これでVue Routerのセットアップは完了です
Vuexのセットアップ
こちらも、Vue CLIの作成時にインストールしているのでインストールは不要です
client/src/store
がVuexのファイルになります
こちらも、わかりやすい様にリネームしてstore.js
として、新規ファイルでauth.js
を作成してください
作成したらstore.js
を下記に書き換えてくださいstore.jsimport Vue from 'vue'; import Vuex from 'vuex'; import auth from './auth'; Vue.use(Vuex); export default new Vuex.Store({ modules: { auth } })
auth.js
にも下記を追加auth.jsimport axios from 'axios'; //axiosを今後使うので読み込んでます const state = { } const getters = { } const mutations = { } const actions = { } export default { namespaced: true, state, getters, mutations, actions }
main.js
に下記を追加するmain.jsimport Vue from 'vue'; import App from './App.vue'; import router from './router/router'; import store from './store/store'; //追加 import axios from 'axios'; Vue.use(axios); new Vue({ router, store, //追加 render: h => h(App) }).$mount('#app')以上でVuexのセットアップは終了です
Vue Metaのインストール
Vue Metaを導入することでVueのscript内でheadのlang設定やtitleなどの設定を行えるようになります
今後使っていくので使い方等は追々説明しますので、ここではインストールとセットアップのみ手順にしたがって行ってくださいコマンドにて下記を実行
$ npm install vue-meta
main.js
にて下記を追加するmain.jsimport Vue from 'vue'; import App from './App.vue'; import router from './router/router'; import store from './store/store'; import axios from 'axios'; import VueMeta from 'vue-meta'; //追加 Vue.use(axios); Vue.use(VueMeta, { refreshOnceOnNavigation: true }); //追加 new Vue({ router, store, render: h => h(App) }).$mount('#app')Vuetifyの導入
※Vuetifyを使わない方はここを飛ばしてもらってOKです
$ cd client
に移動して下記コマンドを実行
$ vue add vuetify
コマンドを実行することでclient/plugins
にvuetify.js
が作成されていることを確認してください
また、main.js
に自動的に追加されていることも確認してください(追加されていない場合は追加してください)main.jsimport Vue from 'vue'; import App from './App.vue'; import router from './router/router'; import store from './store/store'; import vuetify from './plugins/vuetify'; //追加されている import axios from 'axios'; import VueMeta from 'vue-meta'; Vue.use(axios); Vue.use(VueMeta, { refreshOnceOnNavigation: true }); //追加 new Vue({ router, vuetify, //追加されている store, render: h => h(App) }).$mount('#app')終わりに
以上でVue CLIとLaravelで今回必要な物のインストールやセットアップなどの環境の構築が終わりました
次回は、今回のTodoリストで使うUserのテーブルとTodoを管理する2つのテーブルのマイグレーションの作成から始めようと思います次のパートはこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part2
- 投稿日:2020-03-17T17:09:20+09:00
【Nuxt.js】Vue Router基礎編:params, queryを使おう
前置き
とっても便利なparams, queryについてご紹介?
・同じコンポーネントを見せたいけど、
カテゴリごとにURLだけを変えたい…
・一覧ページからソートして表示させたい
そんな時に便利です♪params, queryについて
いくつかに分けて書きます✍️
router-linkが分かれば簡単です?
まだ不安な方のためにも
複数の書き方で記載しました?・params, queryの違い
・使うメリット
・クエリパラメーターとは?
・router-link色々params, queryの違い
まずはURLを見るのが
分かりやすいと思います?localhost:3000/param/param?query=123
・パスパラメーター(param)
?より前の部分、省略できない
・クエリパラメーター(query)
?以降の部分、省略できる?例えば
localhost:3000/project123
projectごとにURLを変更
表示ページは同じでコンポーネントで表示分けfilepages/ --| _id/ -----| index.vue?例えば
localhost:3000/events?today=true
events/index.vueの中で
today=trueでソートをかけて表示filepages/ --| events/ -----| index.vueeventsは絶対省略できないですね。
pages/events/index.vueに
行けなくなってしまいます。?today=trueは省略しても
ソートが外れるだけなので
ページはきちんと表示されます♪メリット
# params, queryの違い で書いた通り!
もう少し身近な例でいうと…例えば!
?イベントサイトで一覧を見たい
その中でも1ページ目だけ見たい
ということが、
できちゃいます!!!以前paginationで使ったことがあるので
こちらも確認してみてください?
https://note.com/aliz/n/nd9f344e4686fクエリパラメーターとは
サーバーに情報を送るための文字列のこと✍️
urlの最後に?から始まる文字列をつけて
サーバーにデータが送信されます!
複数のデータを送る時は?で繋げます。localhost:3000/post?id=123
id=123というデータを
サーバーに送信していますね!ではディレクトリ や
表示はどうなるかというと…filepages/ --| post/ -----| index.vueサーバーにデータを送りたいだけなので
表示するページ自体はpages/post/index.vueそこにクエリパラメーターを入れて
パラメーターを参照することで
自分のIDや登録名が見れたり?
フィルターをかけてソートできたり?✨
するわけです!!!router-link色々
paramsやqueryを使う前に?
pathやnameを使った
router-link(nuxt-link)を理解しましょう!
?template内のlink部分だけ書きます?【飛びたいURL】
localhost:3000/home
【ディレクトリ】
filepages/ --| home/ -----| index.vueではvue検証で
nameとpathを見てみましょう?・nameはhome
・pathは/home
ということが分かりますね!
【pathを使った書き方】
◾️router-linkバージョン
index.vue<router-link :to="{path: 'home'}" > home </router-link>◾️$router.pushバージョン
index.vue<button type="button" @click="$router.push('home')" > home </button>【nameを使った書き方】
◾️router-linkバージョン
index.vue<router-link :to="{name: 'home'}" > home </router-link>◾️$router.pushバージョン
index.vue<button type="button" @click="$router.push({ name: 'home'})" > home </button>さあ、ここまではOKですか???
router-linkにparamsとqueryを追加
ではname, pathに
paramsとqueryを追加してみましょう!
基本的な書き方は公式のこちら。https://router.vuejs.org/ja/guide/essentials/navigation.html
paramsの追加
【path + params を使った書き方】
pathはURLを書けば良いだけ?
こんがらがったら
# params, queryとは
に戻りましょう!【飛びたいURL】
localhost:3000/post/profile
【ディレクトリ 】
filepages/ --| post/ -----| profile.vue◾️router-linkバージョン
index.vue<router-link :to="{ path: '/post/profile'}" > home </router-link>◾️$router.pushバージョン
index.vue<button type="button" @click="$router.push({ path: 'post/profile'})" > home </button>◾️動的ルーティング
user.idごとにページを変えたい(_id.vue)
その場合はuser.idを
fetchでstoreから取ってきたりします!
その辺は別記事にて書こうと思います?
一旦リンクの書き方のみ。✅'' にurlを書いていましたが、
動的ルーティングの場合は
``を使ってかいていきます✍️https://ja.nuxtjs.org/guide/routing/
index.vue<router-link :to="{ path: `/post/${user.id}`}" > home </router-link>【name + params を使った書き方】
【飛びたいURL】
localhost:3000/post/123
【ディレクトリ 】
filepages/ --| post/ -----| _id.vue◾️router-linkバージョン
index.vue<router-link :to="{ name: 'post-id', params: {id: 123} }" > home </router-link>◾️$router.pushバージョン
index.vue<button type="button" @click="$router.push({ name: 'post-id', params: {id: 123} })" > home </button>✍️書き方パターン
・pathはurlをそのまま書く
・nameは追加でparams指定✍️実際の書き方
・name
URLの/を-にする
_id.vueはidにする
・pathindex.vue{ path: 'hoge/hoge' } または { path: `hoge/${変数}`}
queryの追加
【path + query を使った書き方】
【飛びたいURL】
localhost:3000/post?id=123
【ディレクトリ 】
filepages/ --| post/ -----| index.vue◾️router-linkバージョン
index.vue<router-link :to="{ path: '/post?id=123'}" > home </router-link>◾️$router.pushバージョン
index.vue<button type="button" @click="$router.push({ path: 'post?id=123'})" > home </button>【nama + query を使った書き方】
【飛びたいURL】
localhost:3000/post?userId=123
【ディレクトリ 】
index.vuepages/ --| post/ -----| index.vue◾️router-linkバージョン
index.vue<router-link :to="{ name: 'post', query: {userId: 123} }" > home </router-link>◾️$router.pushバージョン
index.vue<button type="button" @click="$router.push({ name: 'post', query: {userId: 123} })" > home </button>まとめ
すごく簡単にまとめると
この2点が分かれば基礎は⭕️です!・nameとpathが
ディレクトリ のどこにあたるか
・params, queryはURLのどこにあたるか記事が公開したときにわかる様に、
note・Twitterフォローをお願いします?
https://twitter.com/aLizlab
- 投稿日:2020-03-17T16:21:55+09:00
Vueのプロジェクトを作成してみるだけの記事
初めに
Vueのプロジェクト作成だけで躓いたを書いた際にそもそも古いバージョンのvue-cliを使っていたことを知り書き直しました。
VueJS触れるときのテンプレートとして作りました。
プロジェクト作成→ローカルでの実行→テストの実行 ができることを目標とします。実施環境
$ sw_vers ProductName: Mac OS X ProductVersion: 10.15.3 BuildVersion: 19D76 $ node -v v13.9.0 $ npm -v 6.14.2Vueを使えるようにするまで
npmを使ってVueCLIをインストールする
(今回はグローバルインストールしていない)
mkdir VueJsProjects cd VueJsProjects npm init -y npm install @vue/cliVueのパスを通す
sed -i '' 's/\"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"/\"vue\": \"vue\"/g' package.jsonプロジェクトを作成する
以下の設定でプロジェクトを作成していきます。
$ npm run vue create hogehoge Vue CLI v4.2.3 ? Please pick a preset: Manually select features ? Check the features needed for your project: Babel, Router, Linter, Unit, E2E ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes ? Pick a linter / formatter config: Basic ? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)Lint on save ? Pick a unit testing solution: Jest ? Pick an E2E testing solution: Cypress ? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files ? Save this as a preset for future projects? Yes ? Save preset as: hogehoge_preset ? Pick the package manager to use when installing dependencies: Yarn ...(中略) ? Successfully created project portfolio. ? Get started with the following commands: $ cd hogehoge $ yarn serveunittest参考:https://vue-test-utils.vuejs.org/ja/guides/choosing-a-test-runner.html
E2E参考:https://qiita.com/os1ma/items/5429cd8e12ac43a6a803実行の前に
yarnを使っていないのでインストールから始めます。
$ brew install yarn $ yarn --version 1.22.4実行
cd hogehoge yarn serveGit管理
gitリポジトリは自動的に作成されている…。
$ git status On branch master nothing to commit, working tree clean自動テスト
unittest
$ yarn test:unit yarn run v1.22.4 $ vue-cli-service test:unit PASS tests/unit/example.spec.js HelloWorld.vue ✓ renders props.msg when passed (29ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 2.007s Ran all test suites. ✨ Done in 5.50s.e2eテスト
$ yarn test:e2e後処理
pushするならする
git remote add origin [作成済みのリポジトリを使用] git push -u origin masterまとめ
普通に、簡単に、VueJsのテスト実行までできました。
前回のようにバージョンミスとかで無駄に時間を取られないように注意していきたいですね。
- 投稿日:2020-03-17T16:17:49+09:00
【Circle CI】Nuxt.jsのdotenvを設定して環境変数をSTGとPRDで出し分けする
Nuxt.jsで自動デプロイする時にSTGとPRDを出し分ける
現在開発のプロジェクトではNuxt.jsを利用していて、Circle CIを使ってGKEに対して自動デプロイをしています。
参考:https://qiita.com/arthur_foreign/items/6ac67596a98c0a60d6be
ただ、環境変数をSTGとPRDに分けるには不十分な手順だったため、備忘のため新しく記事を書くことにしました。
Circle CIで環境変数をSTGとPRD向けに設定する
Environment Variablesで、STGとPRD向けに作った環境変数を.envファイルに書き込むための布石を打っておきます。
別に分けられれば名前はどうでもいいんですが、以下の命名規則にしました。
- STG環境で利用する環境変数 =>
STG_環境変数名
- PRD環境で利用する環境変数 =>
PRD_環境変数名
Nuxt.jsの.envに環境変数を書き込むコマンド
Nuxt.jsのCSR時のAPIの向き先とSENDGRIDのAPI KEYを、STGとPRDで出し分けした際に以下のコマンドを書き込みました。
ECHO = echo inject_envfile_stg: $(ECHO) API_URL_BROWSER=${STG_API_URL_BROWSER} > .env $(ECHO) SENDGRID_API_KEY=${STG_SENDGRID_API_KEY} >> .env inject_envfile_prd: $(ECHO) API_URL_BROWSER=${PRD_API_URL_BROWSER} > .env $(ECHO) SENDGRID_API_KEY=${PRD_SENDGRID_API_KEY} >> .env※Makefileを使っています。
参考:https://qiita.com/arthur_foreign/items/6ac67596a98c0a60d6be
以下の記事を参考にしました。
参考:https://blog-hello-world.web.app/posts/2019-12-07-nuxt-dotenv-module-circleci/
- 投稿日:2020-03-17T13:25:05+09:00
Docker上でVueを動かすときに環境変数が読まれない
なにが起きましたか
- 環境
- Docker上
- @vue/cli
npm run build
でビルドhttp-server dist/
で配信Vueのフロントエンドを作っている際,サーバサイドのAPIのエンドポイントを環境変数に格納して動かす方式を取っていた.
ローカルのビルドではちゃんと環境変数が読まれるが,Azure上にイメージをpushしたビルドでは環境変数が読まれないという状況原因
原因は主に2つあった.
- そもそも,クライアントに配信されるjsに環境変数って????
@vue/cli
には,ビルド時にコード中の環境変数を参照する部分を置き換えてくれる機能があるVUE_APP_*
で始まる環境変数のみ置き換えてくれる.それ以外はundefined
になる.これは既にそのとおりにしており,
VUE_APP_API_ENDPOINT
にサーバ側URLを設定していた.しかし,それでも環境変数は読まれない.
- Dockerfileで環境変数を設定していたが,ビルド後に環境変数を設定していた.
つまりこんな感じに書いていた
Dockerfile.productionFROM node:lts-alpine RUN npm install -g http-server && \ npm install && \ npm run build EXPOSE 8000 ENV VUE_APP_API_ENDPOINT='https://hogehoge.net' CMD [ "http-server", "dist", "-p", "8000", "-g", "-b" ]なんとなく「環境変数はランタイムで読み込まれるもの」という思い込みが原因であった.加えて,ローカルでは
npm run serve
で動作していたため,これは実際にランタイムで(コマンドを叩いたタイミング)で環境変数が読み込まれており,何も問題がなかったこともある.
ちゃんとビルドコマンド前にENV ~~~
を移動させると,環境変数を参照することができた.結論
- クライアントサイドかつDockerでの開発のときは,ENVをビルドコマンドの前に書こう!
- 実行時に必要ないし,
RUN
のときに直接指定してもいいかもしれない.そのほうが意味的には良い- 思い込み怖いね
- 投稿日:2020-03-17T12:43:59+09:00
Vueのプロジェクト作成だけで躓いた
追記
記事内で
vue-cli
を使っていますが古いバージョンのようです。
@vue/cli
を使用して再度書き直すつもりです。そしたら躓くこともないのかも…!
-> 書き直しました。すんなりできました。初めに
VueJS触れるときのテンプレートとして作りました。
…がinitした状態のままテスト実行したら躓いたのでその対処法までです。実施環境
$ sw_vers ProductName: Mac OS X ProductVersion: 10.15.3 BuildVersion: 19D76Vueを使えるようにするまで
npmを使ってVueCLIをインストールする
(今回はグローバルインストールしていない)
mkdir VueJsProjects cd VueJsProjects npm init -y npm install vue-cliVueのパスを通す
sed -i '' 's/\"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"/\"vue\": \"vue\"/g' package.jsonプロジェクトを作成する
npm run vue init webpack hogehoge ? Project name hogehoge ? Project description vue project ? Author hogehoge@mail.com ? Vue build standalone ? Install vue-router? Yes ? Use ESLint to lint your code? Yes ? Pick an ESLint preset Standard ? Set up unit tests Yes ? Pick a test runner jest ? Setup e2e tests with Nightwatch? Yes ? Should we run `npm install` for you after the project has been created? (recom mended) npm ... # インストール開始 To get started: cd hogehoge npm run dev実行
cd hogehoge npm run devGit管理
ここではGitを使いバージョン管理を行う
git init git add . git commit -m "first commit."CI
CI/CDについても気になったので試してみる
npm testセキュリティエラーでFAILに…。
FAIL test/unit/specs/HelloWorld.spec.js ● Test suite failed to run SecurityError: localStorage is not available for opaque origins調べてみるとjestのバージョンの問題の可能性があるらしい。
test/unit/jest.conf.jsmodule.exports = { verbose: true, testURL: "http://localhost/", ... }上記を追加して再度実行
npm test... PASS test/unit/specs/HelloWorld.spec.js HelloWorld.vue ✓ should render correct contents (23ms) ... Running: default e2e tests Error retrieving a new session from the selenium server Connection refused! Is selenium server started? { value: { message: 'session not created: Chrome version must be between 71 and 75\n' + ...jestは通ったがe3eでエラー。
chromedriverのバージョンをチェックする$ npm list | grep chromedriver ├─┬ chromedriver@2.46.0$ npm info chromedriver versions [ ... '2.46.0', '73.0.0', '74.0.0', '75.0.0', '75.0.1', '75.1.0', '76.0.0', '76.0.1', '77.0.0', '78.0.0', '78.0.1', '79.0.0', '79.0.1', '79.0.2', '79.0.3', '80.0.0', '80.0.1' ]chromedriverをアップデートする。
chromeは普段最新版しか使わないのでドライバーも最新版を使うようにする。sed -i '' 's/\"chromedriver\": \"^2.27.2\"/\"chromedriver\": \"latest\"/g' package.json npm install -dev npm list | grep chromedriver├─┬ chromedriver@80.0.1アップデートが完了したので再度実行
npm testRunning: default e2e tests { parser: "babylon" } is deprecated; we now treat it as { parser: "babel" }. ✔ Element <#app> was visible after 103 milliseconds. ✔ Testing if element <.hello> is present. ✔ Testing if element <h1> contains text: "Welcome to Your Vue.js App". ✔ Testing if element <img> has count: 1 OK. 4 assertions passed. (8.794s)テストが実行できるようになったので後処理。
git add . git commit -m "hoge" git remote add origin [作成済みのリポジトリを使用] git push -u origin masterまとめ(というか感想)
Vueをインストールしてテスト&実行するだけですが割と手順が必要でした。
(そのままテストくらいパスするようにならないかな…?)
解決してまとめてみるとこれくらいの分量ですがnpmをあまり触っていなかったこともあり結構ハマりました。
- 投稿日:2020-03-17T10:53:54+09:00
vuetifyのスクロールイベントでトップへ戻るボタンを作る
vuetifyにはデフォルトでスクロールイベントを起こす機能があり、その機能を組み合わせることによりトップへ戻るボタンを作ることができます。
vue.jsでは「vue-go-top」というページ戻り専用のライブラリーはありますが、今回はvuetifyのみを利用して作成します。
クリックしたらTopに戻る
vuetifyには
$vuetify.goTo()
と指定するだけでスクロールイベントを起こすことができます。
スクローリングディレクティブ<v-btn @click="$vuetify.goTo(target, options)"></v-btn>こちらに0をいれると、クリックしたときに一番上まで戻ります。
<v-btn @click="$vuetify.goTo(0)"></v-btn>scriptに記入する
$vuetify.goTo(0)
を下記のようにscriptに記入することでtemplate側をすっきりできます。<v-btn @click="toTop"></v-btn>methods: { toTop () { this.$vuetify.goTo(0) } }スクロールしたら表示される
こちらはv-scrollを使用します。
スクロールで呼び出す関数をセットします。
<v-btn v-scroll="onScroll" @click="$vuetify.goTo(0)"> </v-btn>※デフォルトはウィンドウですが、スクロールの変更を監視するターゲットは任意のIDセレクターに変更できます。
methodsに記入していく
onScroll関数に記入。
typeof window === 'undefined'
はターゲットのデフォルトがwindowの場合使用。
window.pageYOffset
で垂直スクロール量を取得target.scrollTop
も同様です。methods: { onScroll (e){ if (typeof window === 'undefined') return const top = window.pageYOffset || e.target.scrollTop || 0 } }表示・非表示の設定
v-showで表示・非表示を切り替えます。
さきほどのスクロール値を取得してfabに格納。指定の値にきたら表示するようにします。<v-btn v-scroll="onScroll" v-show="fab" @click="$vuetify.goTo(0)"> </v-btn>data: () => { return { fab: false }; }, methods: { onScroll (e){ if (typeof window === 'undefined') return const top = window.pageYOffset || e.target.scrollTop || 0 this.fab = top > 500 } }すべてまとめて表示する
さきほどの機能をまとめ、装飾やアニメーションを追加するとのようなソースになります。
少し動きも付けたかったのでtransition
を使ってみました。<transition name="fade"> <v-btn v-scroll="onScroll" v-show="fab" fab dark fixed bottom right color="primary" @click="toTop"> <v-icon>fas fa-angle-up</v-icon> </v-btn> </transition> <script> export default { data: () => { return { fab: false }; }, methods: { onScroll (e){ if (typeof window === 'undefined') return const top = window.pageYOffset || e.target.scrollTop || 0 this.fab = top > 500 }, toTop () { this.$vuetify.goTo(0) } } } </script> <style scoped> .fade-enter-active, .fade-leave-active { transition: 0.5s; } .fade-enter, .fade-leave-to { opacity: 0; transform: scale(0); } </style>これで外部ライブラリを使わずvuetifyの機能のみでページへ戻るボタンが作れます。
- 投稿日:2020-03-17T08:19:43+09:00
NFCシールを活用して自動打刻ツールを(個人的に)作ってみた話
はじめに
今回はNFCシールを使用して会社の自動打刻システムを、完全に自分用で作ります!
要件定義
なぜつくるか?
現在、弊社の勤怠は、エクセルで管理されています。
実際の打刻フローとしては、
- 出社したら出社時刻をエクセル開いて手動で打刻
- 保存
- 退社するときに退社時刻をエクセル開いて手動で打刻
- 保存
。。。
毎日エクセルポチポチするの面倒すぎる!!!!!!!!!作業自体も面倒なのに、何日か打刻を忘れるとまあ面倒臭いことになります。
せっかくIT企業にいるんだからいろいろスマートにやりたい...
ということで、今回の自動打刻システムの開発を決意しました。どう作るか?
今回開発する自動打刻システムでは、NFCシールを活用していきます。
処理の流れとしては、
- NFCシールにスマホをかざして専用のWEBサイトを表示する
- WEBサイトから自動打刻システムにリクエストを投げる
- 打刻する
といった感じにしようかと思います。
NFCシールにスマホかざすのとリクエストを投げるところにWEBサイト表示をはさんでいるのは、
意図しない打刻を防ぐためです。クライアント側で打刻される時刻の確認、出社なのか退社なのかの選択できた方が、
手順は増えますが確実かなあということでワンクッションはさみました。また、現状では個人用なのでユーザーの識別は行いません。
どう使うか?
想定される使用フローは下記のとおりです。
- 出社したらデスクのどこかしらに貼ったNFCシールにスマホをかざす
- 表示されるWEBサイトで打刻時間を確認、出社ボタンを押す
- (退社時も同じ)
かなりスマート...!(な気がする)
使用技術
インフラはAWSの各サービスを利用します。
メイン処理の部分にはLambda、エクセルファイル(勤怠管理)はS3に保存します。
サーバーサイドにnode.js、WEBフロントエンドにはVue.jsを使用しつつ、HTTP通信はaxiosを使用します。
なぜNFCシールを使うか?
ただ使ってみたかった。
実は今回の開発、個人的にNFCシールを使ったアプリを作ってみたかったので、NFCシールありきで考えていました。
スマホかざすだけで打刻できるのステキじゃん...
なぜnode.jsか?
一番の理由は、Lambdaがnode.jsで書けるからです笑
他にも
java, ruby, pythonなどなど、いろんな言語で書くことができます。フロントエンド開発に興味があり、日頃からJavaScriptを勉強しているので、
サーバーサイドもJavaScriptで書こう!ということでnode.jsを選びました。なぜLambdaか?
Lambdaとは、AWSが提供するサーバレスアーキテクチャを構築するためのサービスです。
通常は、EC2インスタンスは常時存在し、アプリケーションも常時起動されているのですが、Lambdaはリクエストが送られてきたときのみインスタンスを生成→アプリケーションを実行→インスタンスを破棄という挙動をします。
今回作成するアプリは常時起動している必要もないので、コストを抑える意味でもLambdaが適しているのではないかと考えました。
なぜVue.jsか?
個人的に使い慣れているのでフロントはVue.jsで書きます。
と言っても、現在時刻を表示するのと、ボタンを二つ配置するだけなので全く難しいことはしません笑
強いて言えば、ローディングのアニメーションを作り込むくらいでしょうか...。
HTTP通信はaxiosを使用します。
設計
アーキテクチャ図
アーキテクチャの全体像としては、下記の通りです。
構成は至ってシンプルで、
HTTPリクエストをAPI Gatewayで受け付け、Lambdaに投げます。勤怠を管理しているエクセルはS3においておき、Lambdaからそのエクセルファイルに書き込みをしていく感じです。
処理が完了すると、処理結果をSuccessかFailでクライアントに通知します。
アプリケーションの実装
自動打刻システム(node.js)実装
コードの全貌は下記のGitHubリポジトリを御覧ください。
GitHub リポジトリ
エクセルファイルの操作には、「xlsx-populate」というライブラリを使用しました。
最初は「xlsx」というライブラリを使って実装していましたが、このライブラリだと処理をして、保存するとマクロや書式が無効化された状態になってしまうのでつかえず...。
個人的にはドキュメントも「xlsx-populate」のほうが読みやすかったです!
処理としてはファイルを読み込んで、シートを指定して、セルを指定して値を書き込み、保存しているだけです。弊社の勤怠表は月ごとにシートが分かれているので、処理の頭でDateオブジェクトを生成して、得られた各値でシートや記入するセルを判定しています。
また、弊社は30分ごとに勤務時間として打刻できるので、打刻する時刻を30分単位に変換する関数を用意しています。
今後もっと本格的に運用していくことになったら、このあたりで拡張の余地がありますね。
実装で苦労したのは非同期処理とAWS S3からファイルを取得して、書き込んだものをアップロードし直す処理のところ。
const params = { Bucket: 'バケット名', Key: 'キー' } s3.getObject(params, (err, data) => { }上記のように記述すれば、指定されたバケットのオブジェクト(ファイル)を取得できて、data変数に格納されます。
また、アップロードするときは、
const params = { Bucket: 'バケット名', Key: 'キー', Body: 'アップロードしたいファイル' } s3.putObject(params, (err, data) => { })でアップロードできます!
ここがnode.jsの情報がなかなか転がってなくて苦労しました。
取得も書き込みも注意点としては、取ってきたり送信するためには、データ形式に気をつけなければなりません。
今回僕は、これらの処理の前後にエクセルファイルをバッファーに変換する処理をはさみ、変換したものをparams変数のBodyとしています。
クライアントサイド(vue.js)実装
クライアントサイド(WEB)はVue.jsで作りました。
最終的には静的サイトとしてビルドして、Netlifyでホスティングします。
こちらは特に難しいことはしていません。
UIはVuetifyを使ったので適当に作った割には整っています。
スクショですが、下記のようになりました。
打刻すると、vue-loading-templateを使用したアニメーションが流れて、レスポンスが帰ってくるとアラートが表示されます。
(若干左によってるのはスクショが下手だからです...笑)
インフラ環境構築
構築したもの
いよいよインフラの構築に入ります。今回は、メインのAPIをLambdaで動かします。
勤怠表(エクセルファイル)はS3にアップロードしておき、Lambdaから読み取り、書き込みを行います。
HTTPリクエストの受け口として、APIGatewayを配置します。
ここに想定されるリクエストがとんできたら、それをトリガーにLambda関数が動く仕組みにしていきます。
また、クライアントサイド(WEBアプリ)はNetlifyという静的サイトのホスティングサービスを利用します。
Netlifyに関しては後日別記事で言及します。
ハマったポイント
私はインフラ超初心者なので、インフラ構築でかなりつまづきました...。
「LambdaからS3のファイルをとってこれない」
作成したLambdaに正しくロールを付与していなかったため、アクセス権限 is 何の状態が1時間くらい続きました...。
「Lambda関数(メインAPI)が非同期処理になっていて肝心の処理を行う前にLambdaが終了してしまっていた」今回使用した「xlsx-populate」は非同期処理をすることが前提のライブラリです。
恥ずかしながら、node.jsだけでなく非同期処理の知識も乏しく、Lambdaはエラーなく終了するのに肝心の処理が実行できてない...。
という状態で約5日間潰しました。
エラー箇所の切り出しが下手だったなあと反省しています。
いろんな記事や書籍を読み漁りつつ、async awaitを駆使してなんとか解決しました。
「CROS」
実際にクライアントサイドからリクエストを投げる時にはまりました。
今までなーんとなくしか理解してなかったですが、これを機にしっかり学べました。
CROSに関しては別記事でまとめます。
実際につかってみた
明日から出社と退社が楽しみになりそう。
(ちょっと処理は遅いですが)個人的にほぼノンストレスに打刻できるようになったので満足です。
ただ、本当にきちんと勤怠をつけるためには小難しい会社のルールがあるみたいなので、そのうちきちんとしたものも作りたいです。
今のところはほぼ毎日きっちり定時に退社しているので細かい調整はそんなに必要なさそう...だと思ってます笑
おわりに
NFCさいこう!!!!!たのしい!!!!!
- 投稿日:2020-03-17T08:09:49+09:00
Trello風Webアプリケーションを作成してみた
作ったもの
最近、プログラミングから少し離れていたので、思い出すこともかねてTrello風のWebアプリケーションを作ってみた。
▼デモ( https://x-color.github.io/vue-trello )
使用技術
フロントエンド
フロントエンドはSPAとなっており、Vue.jsで実装している。
- Vue.js: JavaScriptフレームワーク
- Vuex: Vue.js用状態管理ライブラリ
- Vue Router: SPA構築用のルーター
- Vuetify: Vue.jsのマテリアルデザインコンポーネントフレームワーク
- Vue.Draggable: ドラッグアンドドロップ処理用ライブラリ
バックエンド
バックエンドはAPIサーバーとなっており、Go言語で実装している。
実装内容(カードを動かす処理)
今回のアプリケーションで実装したカードを動かす処理の概要を以下で紹介していく。
基本的な実装
今回はカードを動かす処理に、Vue.Draggableを用いているため、
draggable
タグで動かしたいものを囲むだけで実装可能。
以下の例は、fruits
配列をドラッグアンドドロップで自由に並び替える処理。<template> <draggable v-model="fruits"> <!-- ここの要素がドラッグアンドドロップ可能になる --> <div v-for="(v, i) in fruits" :key="i">{{ v }}</div> </draggable> </template> <script> import draggable from 'vuedraggable' export default { components: { draggable, }, data() { return { fruits: [ "apple", "banana", "cherry" ], } } } </script>Vuexで管理しているデータを並び替える
今回の場合は、カードのデータや並び順をVuex内で管理しているため、Vuex内のデータを並び替える必要がある。
公式のREADMEに記載されている通り、computed
内からVuexのstate
を呼び出し、Setterを用いて更新することで対応可能。
シンプルな配列を並び替えたいときは、以下のようにするだけで並び変え可能。<template> <draggable v-model="list"> <div v-for="(v, i) in list" :key="i">{{ v }}</div> </draggable> </template> <script> import draggable from 'vuedraggable' export default { components: { draggable, }, computed: { list: { get() { return this.$store.state.list }, set(value) { this.$store.commit('updateList', value) }, }, }, } </script>今回作成したアプリでは、カードを並び替えた際に一部データの更新を行う必要があったので、以下のようにメソッドを呼び出し、データの更新処理を行ったあとにデータの移動を反映させる形にした。
computed: { lists: { get() { return this.getListsByBoardId(this.id); // list の配列を取得 }, set(value) { this.moveList(value); // list が移動した際に行う処理を実施 }, }, }実際には、移動したデータを追跡し、順番を保持している変数値の更新とAPIサーバーとの通信などを実施している。
動かせるものを指定する
<draggable>
で囲った要素は基本的にすべて、ドラッグ可能となってしまう。そのため、動かせないものを一緒にタグで囲わなければならない場合、動かしたいものを指定する必要がある。例えば今回の場合は、カードを追加するための「+」ボタンのカードを動かしたくなかった。以下は失敗例のサンプル。
<draggable>
の中に動かしたいカードと動かしたくない「+」ボタンが入ってしまっている。そのため、このままだとボタンがドラッグ可能となってしまう。<draggable v-model="lists"> <v-col v-for="(list, i) in lists" :key="i" cols="auto"> <card-list :id="list.id" /> </v-col> <!-- カード追加ボタン --> <v-col cols="auto"> <v-btn> <v-icon>mdi-plus</v-icon> </v-btn> </v-col> </draggable>これを改善したのが以下のサンプル。Vue.Draggableでは、
draggable
属性を用いて、動かしたいものと動かしたくないものを対象のclass属性で判別することが可能。<!-- dragable属性(draggable=".item")を付与 --> <draggable v-model="lists" draggable=".item"> <!-- ドラッグ可能にするためにclass属性(class="item")を付与 --> <v-col v-for="(list, i) in lists" :key="i" cols="auto" class="item"> <card-list :id="list.id" /> </v-col> <!-- itemクラスが付与されていないためドラッグ不可 --> <v-col cols="auto"> <v-btn> <v-icon>mdi-plus</v-icon> </v-btn> </v-col> </draggable>上記の例では、
draggable=".item"
を用いて、item
クラスを付与されているもののみ移動可能としている。
これにより、「+」ボタンカードを除いたカードのみ移動可能とすることができる。ドラッグ可能な箇所を指定する
先ほどのサンプルを再掲。
以下のコードだと、実はカード外部でドラッグ可能となってしまう。
ドラッグ対象が<v-col>
となっているので、実際ドラッグしたい<card-list>
外部でもドラッグが可能となってしまい、UI的にカードではない部分でドラッグできてしまう。<draggable v-model="lists" draggable=".item"> <!-- ドラッグ対象は以下の要素となってしまう --> <v-col v-for="(list, i) in lists" :key="i" cols="auto" class="item"> <!-- ドラッグしたいカード --> <card-list :id="list.id" /> </v-col> <v-col cols="auto"> <v-btn> <v-icon>mdi-plus</v-icon> </v-btn> </v-col> </draggable>改善した結果が以下のサンプルとなる。
handle
属性を用いて、ドラッグ判定を出す部分をclass属性で指定することが可能。<!-- handle属性(handle=".handle")を付与 --> <draggable v-model="lists" draggable=".item" handle=".handle"> <v-col v-for="(list, i) in lists" :key="i" cols="auto" class="item"> <!-- class属性(class="handle")を付与 --> <card-list :id="list.id" class="handle" /> </v-col> <v-col cols="auto"> <v-btn> <v-icon>mdi-plus</v-icon> </v-btn> </v-col> </draggable>上記の例では、
handle=".handle"
を用いて、handle
クラスを付与されているものがドラッグ可能と判定している。これにより、<card-list>
内(カード)をドラッグした場合のみドラッグ可能とすることができる。スムーズなドラッグアニメーションにする
デフォルトの移動時のアニメーションだと、動かしたというよりも瞬間移動した感じが出てしまう。そのため今回は、アニメーションを変更し、スムーズに動かした感じを出すこととした。
<!-- animation属性(:animation="300")を付与 --> <draggable v-model="lists" draggable=".item" handle=".handle" :animation="300"> <v-col v-for="(list, i) in lists" :key="i" cols="auto" class="item"> <card-list :id="list.id" class="handle" /> </v-col> <v-col cols="auto"> <v-btn> <v-icon>mdi-plus</v-icon> </v-btn> </v-col> </draggable>
animation
属性を用いてアニメーションの時間を変更している。今回は時間を多めにとることにより、動いている感じを出している。アニメーションも属性を一つ追加すればよいだけなのでとても簡単にできる。最後に
久々のプログラミングだったので、細かなところなどを結構忘れていて、実装に時間がかかってしまった。
また、初めてドラッグアンドドロップを実装したが、Vue.Draggableを用いることで簡単に実装することができた。ドラッグアンドドロップを実装したい場合はとてもおすすめ。
- 投稿日:2020-03-17T08:09:49+09:00
GoとVue.jsでTrello風Webアプリケーションを作成してみた
作ったもの
最近、プログラミングから少し離れていたので、思い出すこともかねてTrello風のWebアプリケーションを作ってみた。
▼デモ( https://x-color.github.io/vue-trello )
使用技術
フロントエンド
フロントエンドはSPAとなっており、Vue.jsで実装している。
- Vue.js: JavaScriptフレームワーク
- Vuex: Vue.js用状態管理ライブラリ
- Vue Router: SPA構築用のルーター
- Vuetify: Vue.jsのマテリアルデザインコンポーネントフレームワーク
- Vue.Draggable: ドラッグアンドドロップ処理用ライブラリ
バックエンド
バックエンドはAPIサーバーとなっており、Go言語で実装している。
実装内容(カードを動かす処理)
今回のアプリケーションで実装したカードを動かす処理の概要を以下で紹介していく。
基本的な実装
今回はカードを動かす処理に、Vue.Draggableを用いているため、
draggable
タグで動かしたいものを囲むだけで実装可能。
以下の例は、fruits
配列をドラッグアンドドロップで自由に並び替える処理。<template> <draggable v-model="fruits"> <!-- ここの要素がドラッグアンドドロップ可能になる --> <div v-for="(v, i) in fruits" :key="i">{{ v }}</div> </draggable> </template> <script> import draggable from 'vuedraggable' export default { components: { draggable, }, data() { return { fruits: [ "apple", "banana", "cherry" ], } } } </script>Vuexで管理しているデータを並び替える
今回の場合は、カードのデータや並び順をVuex内で管理しているため、Vuex内のデータを並び替える必要がある。
公式のREADMEに記載されている通り、computed
内からVuexのstate
を呼び出し、Setterを用いて更新することで対応可能。
シンプルな配列を並び替えたいときは、以下のようにするだけで並び変え可能。<template> <draggable v-model="list"> <div v-for="(v, i) in list" :key="i">{{ v }}</div> </draggable> </template> <script> import draggable from 'vuedraggable' export default { components: { draggable, }, computed: { list: { get() { return this.$store.state.list }, set(value) { this.$store.commit('updateList', value) }, }, }, } </script>今回作成したアプリでは、カードを並び替えた際に一部データの更新を行う必要があったので、以下のようにメソッドを呼び出し、データの更新処理を行ったあとにデータの移動を反映させる形にした。
computed: { lists: { get() { return this.getListsByBoardId(this.id); // list の配列を取得 }, set(value) { this.moveList(value); // list が移動した際に行う処理を実施 }, }, }実際には、移動したデータを追跡し、順番を保持している変数値の更新とAPIサーバーとの通信などを実施している。
動かせるものを指定する
<draggable>
で囲った要素は基本的にすべて、ドラッグ可能となってしまう。そのため、動かせないものを一緒にタグで囲わなければならない場合、動かしたいものを指定する必要がある。例えば今回の場合は、カードを追加するための「+」ボタンのカードを動かしたくなかった。以下は失敗例のサンプル。
<draggable>
の中に動かしたいカードと動かしたくない「+」ボタンが入ってしまっている。そのため、このままだとボタンがドラッグ可能となってしまう。<draggable v-model="lists"> <v-col v-for="(list, i) in lists" :key="i" cols="auto"> <card-list :id="list.id" /> </v-col> <!-- カード追加ボタン --> <v-col cols="auto"> <v-btn> <v-icon>mdi-plus</v-icon> </v-btn> </v-col> </draggable>これを改善したのが以下のサンプル。Vue.Draggableでは、
draggable
属性を用いて、動かしたいものと動かしたくないものを対象のclass属性で判別することが可能。<!-- dragable属性(draggable=".item")を付与 --> <draggable v-model="lists" draggable=".item"> <!-- ドラッグ可能にするためにclass属性(class="item")を付与 --> <v-col v-for="(list, i) in lists" :key="i" cols="auto" class="item"> <card-list :id="list.id" /> </v-col> <!-- itemクラスが付与されていないためドラッグ不可 --> <v-col cols="auto"> <v-btn> <v-icon>mdi-plus</v-icon> </v-btn> </v-col> </draggable>上記の例では、
draggable=".item"
を用いて、item
クラスを付与されているもののみ移動可能としている。
これにより、「+」ボタンカードを除いたカードのみ移動可能とすることができる。ドラッグ可能な箇所を指定する
先ほどのサンプルを再掲。
以下のコードだと、実はカード外部でドラッグ可能となってしまう。
ドラッグ対象が<v-col>
となっているので、実際ドラッグしたい<card-list>
外部でもドラッグが可能となってしまい、UI的にカードではない部分でドラッグできてしまう。<draggable v-model="lists" draggable=".item"> <!-- ドラッグ対象は以下の要素となってしまう --> <v-col v-for="(list, i) in lists" :key="i" cols="auto" class="item"> <!-- ドラッグしたいカード --> <card-list :id="list.id" /> </v-col> <v-col cols="auto"> <v-btn> <v-icon>mdi-plus</v-icon> </v-btn> </v-col> </draggable>改善した結果が以下のサンプルとなる。
handle
属性を用いて、ドラッグ判定を出す部分をclass属性で指定することが可能。<!-- handle属性(handle=".handle")を付与 --> <draggable v-model="lists" draggable=".item" handle=".handle"> <v-col v-for="(list, i) in lists" :key="i" cols="auto" class="item"> <!-- class属性(class="handle")を付与 --> <card-list :id="list.id" class="handle" /> </v-col> <v-col cols="auto"> <v-btn> <v-icon>mdi-plus</v-icon> </v-btn> </v-col> </draggable>上記の例では、
handle=".handle"
を用いて、handle
クラスを付与されているものがドラッグ可能と判定している。これにより、<card-list>
内(カード)をドラッグした場合のみドラッグ可能とすることができる。スムーズなドラッグアニメーションにする
デフォルトの移動時のアニメーションだと、動かしたというよりも瞬間移動した感じが出てしまう。そのため今回は、アニメーションを変更し、スムーズに動かした感じを出すこととした。
<!-- animation属性(:animation="300")を付与 --> <draggable v-model="lists" draggable=".item" handle=".handle" :animation="300"> <v-col v-for="(list, i) in lists" :key="i" cols="auto" class="item"> <card-list :id="list.id" class="handle" /> </v-col> <v-col cols="auto"> <v-btn> <v-icon>mdi-plus</v-icon> </v-btn> </v-col> </draggable>
animation
属性を用いてアニメーションの時間を変更している。今回は時間を多めにとることにより、動いている感じを出している。アニメーションも属性を一つ追加すればよいだけなのでとても簡単にできる。最後に
久々のプログラミングだったので、細かなところなどを結構忘れていて、実装に時間がかかってしまった。
また、初めてドラッグアンドドロップを実装したが、Vue.Draggableを用いることで簡単に実装することができた。ドラッグアンドドロップを実装したい場合はとてもおすすめ。
- 投稿日:2020-03-17T08:01:19+09:00
「Scrapboxにコードを書いて実行する」を説明してみる
この記事は株式会社クロノスの「~2020年春~勝手にやりますアドベントカレンダー」の12日目の記事です!
はじめに
今回の投稿はScrapboxのUserScriptテンプレートまとめに引き続き、Scrapboxネタです。(よっぽど好きだということです)
まずは以下のリンク先ページをご覧ください。
Scrapboxにコードを書いて実行するなんとっ Scrapbox上に書いたJavaScriptのコードが実行されてるじゃないですか! すごい。UserScriptととはまた違ったことやってる!
自分もやりたいぃってことで、どうやって実現しているのか調べてみました。※
Vue.js
タグにさせてもらいましたが、Vue.js
自体は実演で使わせてもらっただけで、チュートリアルの域を出ていないです。ご注意ください。知れること・知れないこと
知れること
- Scrapbox上にコード記法で記載したJavaScriptのコードを実行して表示させる方法
知れないこと
- Scrapbox上に書いたJavaScript以外(コンパイルが必要になるような言語)を実行させる方法
- Scrapbox上に実行結果を埋め込む方法
仕組み
どうやって実現させているか紐解く鍵は、
下のコードを実行
のリンクの中身にあります。
遷移した後のURLを見るのでも構いませんが、
https://[アカウント名].github.io/[リポジトリ名]/?code=[ほにゃらら]/[ほにゃらら].js
となっているはずです。Tip1 : CDN
(他にもやりようはあるかとは思いますが)各種プログラムを実行するために、お馴染みのCDN(Contents Delivery Network)を利用します。
Tip2 : GitHub Pages
urlを見てもらったら分かるように、
github.io
の静的ホスティングサービスであるGitHub Pagesを利用します。
(実際は静的ホスティングサービスであれば、何であっても構わないと思うのですが、今回は参考元のやり方のGitHub Pagesで説明させてもらいます)Tip3 : ScrapboxのAPI
公式で大々的に説明があるAPIではありませんが、コード記法で書いた内容にアクセスできるAPIがあります。
形式 :https://scrapbox.io/api/code/[プロジェクト名]/[ページ名]/[付けたファイル名]
普段は上記のようにコード記法
で書いたコードをコピーするために利用されます。
(APIのURLにアクセスすればブラウザ上にコードの中身が表示されます)「Scrapboxにコードを書いて実行する」の仕組みを、一言で(ざっくり)言うなら
静的ホスティングしたサイトにScrapboxで書いたコードを返却するAPIを渡して、コード記法で書かれた該当のコードを書き込み、CDNを利用して実行する
となります。実装方法〜〜ユースケースとともに〜〜
本家Scrapboxにコードを書いて実行するのように、動きのあるコードの方が、面白味があるとは思いますが、今回は
Vue.js
のチュートリアルのコードを実行してみます。GitHub Pagesの準備(簡易説明)
- Github上にリポジトリを作成する。
index.html
ファイルを作成する。- branch :
gh-pages
を作成する- 後はrepositoryのURLにアクセスで
index.html
の内容が表示される!コードの全容
私が今回説明で使っているソースはこちらです。
フォルダ構成├── index.html └── index.jsGithub上に置くファイルはGithub Pagesで準備した
index.html
とindex.js
だけ!!(簡単)
※README.md
ファイルは省略して記載しています。index.js (ScrapboxのAPIのURLをGetパラメータから取得して、画面に読み込ませる)
ここが一番肝要なところですが、
本家様のコードをほぼほぼ流用しています。基本、本家様のコードを参照でお願いします。本家様コード
ちなみにgh-pages
ブランチとmaster
ブランチで内容が違っていたので、見比べて勉強させてもらいました。(jsだけでなくstyle読み込んだりなどなど)今回
Vue.js
のコードを動かしたい関係で、jQueryありきの部分については少し変更を加えています。
今回のindex.jsindex.js// jQueryお馴染みの「$」の部分 $(function(){ ↓ // 純粋なJavaScriptに変更 window.addEventListener('DOMContentLoaded', function(){コードを見てもらったらわかりますが、JavaScriptでアクセスしたURLを解析して、
?code=
の=以降の値(Getパラメータの値)を読み取って、その値をscriptタグとして追加する実装になっています。
(実行する際は?code=[ScrapboxのAPIのURL]
としてアクセスしてください)index.html (必要なCDNの読み込みや簡易のレイアウト記載)
今回はこんなかんじ。
index.html<!DOCTYPE html> <html> <head> <title>Scrapboxでプログラミングの勉強をする</title> <!-- bulma --> <link href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.min.css" rel="stylesheet"> <link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"> </head> <div id="app"> <app-template></app-template> </div> <!-- vue.js --> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script src="./index.js"></script> </html>基本
CDN
と先ほどのindex.js
を読み込んでいるだけ!
CDN
はここで自分の利用したいものをカスタマイズしましょう。
今回はVue.jsと表示の調整でBulmaを書きました。ここまでで下準備は完了!
Scrapboxにコードを書いて実行
では、Scrapboxでコードを書いて実際に実行してみましょう。
コードはVue.jsのチュートリアルを参考に書いています。
Githubに挙げているコードは変更しない方向で、<app-template></app-template>
をカスタム要素として利用した書き方に調整しています。コード記法は拡張子「js」でファイル名書くようにしてください。
- Hello World
const Messages = { template: `<div class="hero">` + '<div class="hero-body has-text-centered">{{ message }}</div>' + '</div>', data: function() { return { message: 'Hello World!!', } } } new Vue({ el: '#app', components: { 'app-template': Messages } })実行の様子。上部は切り取っていますが、実行すると新規タブが開きます。
※押しているボタンについてはおまけで後述します。やってることは
https://[アカウント名].github.io/[リポジトリ名]/?code=[ScrapboxのAPIのURL]
にアクセスするだけです。Scrapboxのコードを実行しているだけなので、Scrapboxのコードを書き換えると、当然結果も変わります。
他の例も用意したので、載せておきます。
- v-for
const BlogPost = { template: `<div class="hero">` + '<div class="hero-body has-text-centered">' + '<div v-for="post in posts" v-bind:key="post.id">' + '{{post.title}}' + '</div>' + '</div>' + '</div>', data: function () { return { posts: [ { id: 1, title: 'My journey with Vue' }, { id: 2, title: 'Blogging with Vue' }, { id: 3, title: 'Why Vue is so fun' } ] } } } new Vue({ el: '#app', components: { 'app-template': BlogPost } })
- ボタン
Vue.component('app-template',{ data: function(){ return { count1: 0, count2: 0 } }, template: '<div class="hero">' + '<div class="hero-body has-text-centered">' + '<button v-on:click="count1++">You clicked me {{count1}} times.</button>' + '<br>' + '<button v-on:click="count2++">You clicked me {{count2}} times.</button>' + '</div>' + '</div>' }) new Vue({ el: '#app' })アクセスするたびに同じ画面だけど違う結果が出てくる
というのはなかなかに面白いものです。
上記の3つのコードを続け様で実行した場合↓
どういった時に使える?
本家にも書いてありますが「プログラミング体験」として抜群です。
自分で勉強するにしても人に教えるにしても、文面と実行できるコードを共存して管理できるというのは、利点が多いのではないでしょうか。(いろいろな使い方ができそうです)注意
言わずもがな、自分のコードは自分のGitHub Pages(あるいは別の静的ホスティングサービス)にアクセス・実行するようお願いします。
といっても他人のページだと
CDN
のカスタマイズもできないですし、自分で用意した方がいろいろ可能性が広がって楽しいと思います。おまけ
開いているページ(前回はカードと書いていた)上のコード記法にアクセスするAPIを抽出して、
https://[アカウント名].github.io/[リポジトリ名]/?code=[ScrapboxのAPIのURL]
を開くUserScript作っているので、参考で載せておきます。
(今までのgif画像でも使っていました)ScrapboxのUserScriptの詳しい説明については前回記事ScrapboxのUserScriptテンプレートまとめやScrapboxのヘルプ参照ください。
下準備
前回記事の番外テンプレート(1)を作成するために、画像を用意して1つページを作ります。
UserScript
UserScriptとはこんな感じ。
script.jsconst GITHUB_REPOSITORY = 'https://[githubユーザ名].github.io/[リポジトリ名]'; const SCRAPBOX_URL = 'https://scrapbox.io'; const $appRoot = $('#app-container'); $appRoot.on('click', 'img[class="icon"]', e => { const $icon = $(e.target).closest('img[class="icon"]'); const iconName = $icon.attr('title'); // 「programming」アイコンの場合に処理を実行 if (iconName === 'programming') { let codeUrl; $('a').each(function (index, atag) { let hrefStr = String($(atag).attr('href')); // getパラメータの作成 if (hrefStr.startsWith('/api/code') && hrefStr.endsWith('js')) { codeUrl = (codeUrl == null) ? 'code=' + SCRAPBOX_URL + hrefStr : codeUrl + '&code=' + SCRAPBOX_URL + hrefStr; } }) console.log('呼び出し先コードのURL :' + codeUrl); window.open(GITHUB_REPOSITORY + '?' + codeUrl); return false; } });これで、Scrapboxの
[programming.icon]
(アイコン記法)と書かれた場所をクリックすれば、プログラムの実行(条件を満たした状態でGitHub Pagesにアクセス)できます!!※ScrapboxのUserScriptは自分にしか効かない(Scrapboxのヘルプ参照)ので、Publicで公開しているプロジェクトにおいて、閲覧者に実行してもらいたい場合等は、リンクをScrapbox上に書くようにしてください。
おわりの余談
前回のブログを書いていた頃含め、1年以上Scrapboxを書いてきたわけですが、アドベントカレンダーやろう! ってなったときも、内容考えるのすごく楽でした。
Scrapboxは項目と項目をつなげることで新たな発想が生まれたり、思考がクリアになったりと、使うほどにいろんな効果があるような気がしています。(個人の感想)ちなみに……
Scrapboxでアウトプットするというのも一つですが、私個人として「Scrapboxの内容をまとめてブログに書く」使い方はこれからもしていくと思います。(私にとってScrapboxはネタ帳の色合いが濃いです)以上です!
- 投稿日:2020-03-17T00:49:58+09:00
入門の次のステップに進めないVue.js学習履歴(随時更新)
概要
ドットインストールの「Vue.js入門」を実施してある程度Vue.jsをわかった気になった。
https://dotinstall.com/lessons/basic_vuejs_v2しかし、実際にリリースされているVue.jsのソースを見るとさっぱりわからなかった。
このため、疑問点と調査経緯を自分のメモ目的でこの記事に残していく。疑問点
yarn run xxx
package.jsonのscriptsで定義されたxxxを実行する。
"scripts": { "xxx": "~~~~~~~~", ←これを実行 "yyy": "~~~~~~~~", : : },■yarn runのドキュメントはこちら。
https://classic.yarnpkg.com/ja/docs/cli/runapp.use(nuxt.render)
expressのミドルウェアとしてNuxt.jsを使う。
const express = require('express') const app = express() : app.use(nuxt.render) :Node.jsの「ミドルウェア」という概念がいまいちわかっていない・・・。
■API: nuxt.render(req, res)
https://ja.nuxtjs.org/api/nuxt-render/■ExpressのミドルでNuxt.jsを利用
https://www.wakuwakubank.com/posts/666-nuxtjs-express-middle/module.exports={}
外部(別ファイル)から参照できるようにする。
module.exports = { mode: 'xxx', router: { base: '/yyy/' },上記の定義をしたjsファイルをrequireすると、jsファイルの中身を参照できる。
■module.exportsとは何か、どうもわからなかったので実験してみた〜Node.jsにて外部moduleをrequireする〜
http://karoten512.hatenablog.com/entry/2018/01/28/191928this.$store.dispatch(~~~)
「$store」はどこにも宣言されていない。
Vuex(ビューックス?)のステートオブジェクトとのこと。
セッションみないたもの??セッションよりは奥が深そう。■Vuexステート
https://vuex.vuejs.org/ja/guide/state.html■Vue.js + Vuexでデータが循環する全体像を図解してみた
https://qiita.com/m_mitsuhide/items/f16d988ec491b7800acethis.$store.dispatch(~~~)その② dispatchについて
「$store.dispatch」でステートのactionを実行(らしい)
actionは非同期処理(らしい)actionの実行とは何か?
this.$store.dispatch(~~~)その③ actionについて
「$store.dispatch」でステートのactionを実行するとしてactionとは何をするのか?
実態が不明。
\store\login.js
にexport const actions
やexport const mutations
があった。
つまり「$store.dispatch」を実行すると、\store\login.js
にexport const actions
のような気がしてきた。async function() {const response = await this.$store.dispatch(~~~)}
asyncで非同期関数。
awaitでpromiseのresolveを実行。つまり、\store\login.js
のactionを非同期実行した結果がresponseに入る?
※このあたり、理解が曖昧。以下などを見て、なんとなく上記の理解。