20190827のvue.jsに関する記事は10件です。

Vuetifyのテーブル(フィルター付き)で合計値を動的に算出する

概要

たとえばこんな表があるとします。

スクリーンショット 2019-08-27 23.11.43.png

合計値と平均値がフッターに表示されています。

ここで、検索キーワードを入れて表をフィルタします。

スクリーンショット 2019-08-27 23.13.12.png

合計値と平均値も更新されています。

解説

Vuetifyのデータテーブルのフィルタ機能は強力ですが、
https://vuetifyjs.com/ja/components/data-tables
更新された値に対してのアプローチは少し工夫が必要です。

コンポーネントのプロパティを確認するとfilteredItemsというものがあります。

スクリーンショット 2019-08-27 23.21.14.png

フィルタされた後のテーブルデータはここに格納されるので活用します。
なお、このテーブルをJSから参照するためにはref属性を使用します。

<v-data-table
    :headers="headers"
    :items="items"
    ref="summary"
>
const summary = this.$refs.summary.$children[0]
summary.filteredItems.forEach((item) => {
  // 合計処理
})

検索キーワードが反映されるたびにトリガーしてみます。

<v-data-table
    :headers="headers"
    :items="items"
    :search="searchTrigger"
    ref="summary"
>
data () {
  return {
    search: '',

    // ...省略
  }
},
computed: {
  searchTrigger: function() {
    this.$nextTick(function() {
      this.calc()
    })
    return this.search
  }
},
methods: {
    calc: function() {
      const summary = this.$refs.summary.$children[0]
      summary.filteredItems.forEach((item) => {
          // 合計処理
      })

    // ...省略

こんな雰囲気で実現することができます。
以下のサンプルコードも合わせてご覧ください。

サンプルコード

https://codepen.io/flatnyat/pen/eYOvPxd

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

Vue.js+ElementUIプロジェクト

書いてあること

  • Vue.jsプロジェクトでElementUIを利用した際のメモ(個人用メモのため間違っている可能性あり・・・)
  • 公式サイトの各コンポーネント実装方法に習って実装

環境

  • CentOS Linux release 7.6.1810 (Core)
  • Node.js v10.16.0
  • Npm 6.10.0
  • Vue 3.9.1
  • element-ui 2.11.1

下記で作成したVue.jsプロジェクトを利用する。
Vue.jsのプロジェクト作成

作成したプロジェクト

↓に置いてあります。
vue-element-project

↓Demo
vue-element-project

準備

必要なパッケージをインストール

bash
$ npm install element-ui --save

ElementUI利用設定

display.cssはレスポンシブレイアウトで要素の表示・非表示切り替えを行う際に使用する。
Utility classes for hiding elements

src/main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import axios from 'axios'
+import ElementUI from 'element-ui'
+import locale from 'element-ui/lib/locale/lang/ja'
+import 'element-ui/lib/theme-chalk/index.css'
+import 'element-ui/lib/theme-chalk/display.css'

Vue.config.productionTip = false

Vue.prototype.$axios = axios

+Vue.use(ElementUI, { locale })

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

Basic

Layout

Layout

span:Gridの列幅
gutter:列の間隔
offset列のオフセット
flex:FlexBoxによる列の配置
xs、sm、md、lg、xl:レスポンシブレイアウトで各ブレークポイントの列の間隔
Utility classes for hiding elements要素の表示・非表示を切り替えるユーティリティクラス

Container

Container

el-container:ラッパーコンテナ。el-header、またはel-footerでネストすると、子要素は垂直に配置される
el-header:ヘッダーのコンテナ。デフォルトのheightは60px
el-aside:サイドバーのコンテナ。デフォルトのwidthは300px
el-main:メインのコンテナ
el-footer:フッターのコンテナ。デフォルトのheightは60px

Color

Color

Button

Button

type:ボタンの色、またはテキストボタンを指定
plain:背景白抜きボタン
round:角丸ボタン
circle:円形ボタン
disabled:無効ボタン
icon:テキストの左側のアイコン。テキストはなくてもよい
i:テキストの右側のアイコン
el-button-group:ボタングループ
loading:ローディングボタン
size:ボタンサイズ
native-type:ボタンタイプ。汎用ボタン、submitボタン、resetボタンを指定

Link

Link

type:リンクの色
disabled:無効リンク
underline:リンク文字列にホバーした際の下線有無
icon:テキスト左側のアイコン
i:テキスト右側のアイコン

Form

Radio

Radio

label:v-modelで指定した変数へ格納する値
disabled:無効
el-radio-group:ラジオボタンをグループ化
el-radio-button:ボタンスタイルで表示
size:ボタンスタイルのサイズ
border:線を付ける

Checkbox

Checkbox

label:v-modelで指定した変数へ格納する値
disabled:無効
el-checkbox-group:チェックボックスをグループ化
indeterminate:全チェック機能で利用
min:チェックの最小数
max:チェックの最大数
el-checkbox-button:ボタンスタイルで表示
size:ボタンスタイルのサイズ
border:線を付ける

Input

Input

maxlength:最大文字数
minlength:最小文字数
disabled:無効
clearable:クリア
show-password:パスワード非表示
prefix-icon:接頭アイコン
suffix-icon:末尾アイコン
rows:Textareaの行数
autosize:Textareaの自動サイズ調整。minRows、maxRowsで最小・最大行数を指定可能
prepend:input内の接頭固定表示
append:input内の接尾固定表示
size:inputのサイズ
el-autocomplete:オートコンプリート

InputNumber

InputNumber

min:最小値
max:最大値
disabled:無効
step:増分
step-strictly:厳密な増分(直接値変更は不可)
precision:精度(小数点以下)
size:inputのサイズ
controls-position:コントロールの位置

Select

Select

disabled:無効
clearable:クリア
multiple:複数選択
collapse-tags:複数選択した際のタグ表示
el-option-group:選択肢のグループ化
filterable:フィルター
remote:リモート検索
remote-method:リモート検索処理
loading:ローディング

Cascader

Cascader

disabled:無効
clearable:クリア
expandTrigger:hoverにすることで、マウスホバー時に下階層を自動で開く
show-all-levels:falseにすることで、最下層レベルのlabelのみ表示
multiple:複数選択
collapse-tags:複数選択した際のタグ表示
checkStrictly:任意のレベルの要素選択を許可
lazylazyLoad:要素を動的にローディング
filterable:フィルター
el-cascader-panel:パネル表示

Switch

Switch

disabled:無効
active-color:アクティブ時の色
inactive-color:非アクティブ時の色
active-text:アクティブ時のテキスト
inactive-text:非アクティブ時のテキスト
active-value:アクティブ時の値
inactive-value:非アクティブ時の値

Slider

Slider

disabled:無効
show-tooltip:ツールチップ
format-tooltip:書式設定
step:増分
show-stops:停止位置の表示
show-input:inputの表示
range:範囲選択
max:最大値
vertical:縦表示
height:高さ
marks:マーカー設定

TimePicker

TimePicker

picker-options:オプション指定。開始時間、終了時間、増分などを指定
arrow-control:矢印選択
is-range:範囲選択
range-separator:範囲選択時のセパレータ文字
start-placeholde:範囲選択時の開始プレースホルダー
end-placeholde:範囲選択時の終了プレースホルダー

DatePicker

DatePicker

picker-options:オプション指定。開始時間、終了時間、増分などを指定
type:日付選択する種類(日付、年、月、週など)を指定
range-separator:範囲選択時のセパレータ文字
start-placeholde:範囲選択時の開始プレースホルダー
end-placeholde:範囲選択時の終了プレースホルダー
default-value:既定値
format:画面表示フォーマット
value-format:変数格納時のフォーマット
default-time:日付選択時に変数へ格納される時刻

DateTimePicker

DateTimePicker

picker-options:オプション指定。開始時間、終了時間、増分などを指定
type:日付選択する種類(日付、年、月、週など)を指定
start-placeholde:範囲選択時の開始プレースホルダー
end-placeholde:範囲選択時の終了プレースホルダー
default-time:日付選択時に変数へ格納される時刻

Upload

Upload

on-success:アップロード成功時の処理
on-preview:プレビュー時の処理
on-remove:削除時の処理
before-remove:削除前の処理
on-exceed:サイズ超過時の処理
on-change:変更時の処理
multiple:複数選択可能
limit:複数選択時の上限ファイル数
file-list:リスト表示方法
show-file-list:リスト表示有無
list-type:リストタイプ
auto-upload:自動アップロード
drag:ドラッグ

Rate

Rate

colors:レートの色
text-color:テキストの色
texts:各レベルのテキスト
show-text:テキスト表示
icon-classes:各レベルのアイコン
void-icon-class:アイコン表示
disabled読み取り専用:
show-score:スコアの表示
score-template:表示するスコアの書式

ColorPicker

ColorPicker

show-alpha:アルファ値を利用
predefine:定義済みカラーを設定
size:サイズ

Transfer

Transfer

data:表示するリスト
filterable:フィルター
filter-placeholder:フィルターのプレースホルダー
filter-method:通常はラベルがフィルター対象となるが、独自の検索メソッドを利用可能
left-defalut-checked:左側のデフォルトチェック
right-default-checked:右側のデフォルトチェック
render-content:表示するリスト
titles:タイトル
button-texts:ボタンラベル
format:チェック個数表示文字列の書式

Form

Form

model:Formコンポーネントで利用するデータ
label:ラベル
label-width:ラベル幅
label-position:ラベルの位置
rules:バリデーションルール
status-icon:バリデーション結果のアイコン表示
autocomplete:オートコンプリート

Data

Table

Table

data:データ
prop:各項目に表示するデータ
label:各項目のラベル
border:罫線
row-class-name:色分け制御
height:ヘッダー固定
fixed:カラム固定
highlight-current-row:ハイライト
sortable:ソート
filters:フィルター
type:表示タイプ

Tag

Tag

type:タイプ
closable:閉じるボタン
disable-transitions:閉じるアニメーション(トランジション)
effect:テーマ

Progress

Progress

percentage:表示する値
format:書式
status:アイコン
text-inside:プログレスバー内部に値を表示
stroke-width:プログレスバーの縦幅
color:色
type:タイプ

Pagination

Pagination

layout:ページネーションのレイアウト(表示方法)
total:総データ件数
page-size:1ページあたりのデータ件数
pager-count:ページネーションを折りたたむ
background:背景色
small:小さく表示
hide-on-single-page:ページネーションが1ページの場合に表示するか

Badge

Badge

value:バッチに表示する数値。文字も指定可能
type:色
max:最大値。指定した数値と+記号が表示される
is-dot:赤いドット表示

Notice

Alert

Alert

type:色
title:タイトル
description:説明
effect:テーマ
closable:閉じるボタンを許容するか
close-text:閉じるボタンのテキスト
show-icon:アイコン
center:中央揃え

Loading

Loading

v-loading:trueの場合、記載したタグに対してローディングを表示
element-loading-text:テキスト
element-loading-spinner:アイコン
element-loading-background:背景色

Message

Message

this.$message:メッセージ表示
type:色
showClose:閉じるアイコン
center:中央揃え
dangerouslyUseHTMLString:HTMLタグを利用

MessageBox

MessageBox

type:メッセージ種類
confirmButtonText:OKボタンテキスト
cancelButtonText:キャンセルボタンテキスト
showCancelButton:キャンセルボタンを表示
inputPattern:入力確認用の正規表現
inputErrorMessage:入力エラー時のテキスト
dangerouslyUseHTMLString:HTMLタグを利用
distinguishCancelAndClose:キャンセル、クローズを区別
center:中央揃え

Notification

Notification

title:タイトル
message:メッセージ
duration:表示する時間
type:種類
position:表示位置
offset:オフセット
dangerouslyUseHTMLString:HTMLタグを利用
showClose:閉じるアイコンを表示しない

Navigation

NavMenu

NavMenu

default-active:デフォルト選択
mode:表示モード
background-color:背景色
text-color:文字色
active-text-color:アクティブメニューの文字色
disabled:無効
collapse:展開/閉じる

Tabs

Tabs

label:表示テキスト
name:名前
type:表示モード
tab-position:表示位置
editable:タブ追加・削除

Breadcrumb

Breadcrumb

separator:セパレータ文字
separetor-class:セパレータアイコン

Dropdown

Dropdown

split-button:スプリットボタン
type:色
disabled:無効
trigger:トリガー
command:コマンド
size:サイズ

Steps

Steps

active:位置
finish-status:終わった際のステータス
space:スペース
align-center:中央揃え
title:タイトル
description:説明
icon:アイコン
direction:表示モード
simple:シンプル表示

Others

Dialog

Dialog

visible.sync:ダイアログ表示有無
before-close:閉じるボタン、ダイアログ外の領域をクリックした際の処理
center:中央揃え

Tooltip

Tooltip

content:表示するテキスト
placement:表示位置
effect:色
disabled:無効

Popover

Popover

placement:表示位置
title:タイトル
content:本文
trigger:表示方法

Card

Card

header:タイトルセクション
body-style:本文セクションのスタイル
shadow:影

Carousel

Carousel

height:カルーセルの高さ
trigger:表示切り替えトリガー
interval:表示切り替え間隔
indicator-position:インジケーターの表示位置
arrow:矢印表示
type:表示タイプ
direction:表示方向

Collapse

Collapse

accordion:アコーディオン表示

Timeline

Timeline

reverse:並び順
timestamp:表示する文字列
icon:アイコン
type:色
color:色
size:サイズ
placement:表示位置

Divider

Divider

direction:方向
content-position:コンテンツの表示位置

Calendar

Calendar

range:表示範囲
first-day-of-week:週の最初の曜日

Image

Image

src:画像ソース
fit:コンテナに合わせる画像サイズ
placeholder(slot):読み込み中の表示
error(slot):読み込みエラーの表示
lazy:遅延読み込み
preview-src-list:プレビュー表示

Backtop

Backtop

visibility-height:表示を開始する位置
right:右からの距離
bottom:下からの距離

Avatar

Avatar

src:画像サイズ
size:大きさ
icon:アイコン

Drawer

Drawer

visible.sync:表示有無
direction:表示する方向
before-close:ドロワーを閉じる前の処理

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

Laravel + Blade + Vue.jsで、Vue.jsのコンポーネントの属性に渡す変数が正しくエスケープされていないとエラーが出る

環境と背景事情

  1. LaravelでVue.jsを使用している(セットアップはこのページを参考にしました)
  2. bladeテンプレート内に、直接Vue.jsのマークアップを表記している。
  3. 上記のbladeテンプレには、Vue.jsコンポーネントを使用しており、属性でテキストデータを渡している。
  4. 上記のコンポーネントで、ユーザが入力したテキストデータを表示させている。

問題

ユーザが入力データに引用符やHTMLコードを用いると、Vue.jsのページのコンパイル時にエラーが発生して、ページが表示されない。

解決策

Vue.jsコンポーネントのテキストデータを渡す属性値に、PHP関数の"json_encode"を用いてエンコードした。

エラー表示例

[Vue warn]: Error compiling template:

HTMLコードをそのままコピペした場合:

avoid using JavaScript keyword as property name: "if"

引用符が混じっているテキスト:

invalid expression: Invalid or unexpected token in

その他:

invalid expression: Unexpected number in
invalid expression: Unexpected string in

コンソール出力例:
Screen Shot 2019-08-27 at 11.16.01.png

問題があったコード例

bladeテンプレートに記述されたVue.jsのマークアップ

<project-list-items 
            :title="{{ $project->title }}"
            :description="{{$project->description }}"
/>

エンコードも何もせずにそのまま値を渡している。

問題解決後のコード例

json_encodeを用いてエンコードした。

<project-list-items 
            :title="{{ json_encode($project->title) }}"
            :description="{{ json_encode($project->description) }}"
/>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails & Nuxt.jsのDocker環境をalpineイメージで構築

Ruby on Rails、Nuxt.js、MySQLのDocker環境を作成します。
Rails、Nuxtのalpine環境の構築手順はそれぞれだと多くあるのですが、まとまったものがあまり ない 見つからなかったので、復習を兼ねてポストを作成します。

準備

ディレクトリ作成

作業ディレクトリは任意です。

$ NEW_APP=rails-nuxt-app #任意のアプリ名
$ mkdir ${NEW_APP}
$ cd ${NEW_APP}
$ mkdir ./backend ./frontend

backend はRails用、frontend はNuxt用のディレクトリです。
まずは下記のファイルを修正していきます。

.
├ backend
│   ├ Dockerfile
│   ├ Gemfile
│   └ Gemfile.lock
│
├ frontend
│   └ Dockerfile
│
├ docker-compose.yml
└ .env

docker-compose.yml

.env

docker-compose.ymlで参照する環境変数を記載します。
ここではMySQLのrootパスワード、RailsおよびNuxt環境のホスト、ポート番号のみ定義します。
RailsとNuxtは、共にデフォルトのポートが 3000 番なので、後の利便性のためにいずれかを変えておきます。
(本記事ではNuxt側を 8080 に変更)

MYSQL_ROOT_PASSWORD=password

BACKEND_HOST=0.0.0.0
BACKEND_PORT=3000

FRONTEND_HOST=0.0.0.0
FRONTEND_PORT=8080

./docker-compose.yml

.envで定義した変数を参照しています。
docker-compose.ymlの environment、 Dockerfileの ENV で同じ環境変数が定義されていた場合は、前者が使用されます。
(本記事ではDockerfile単体でもイメージ作成できるように環境変数の記載を残していますが、docker-compose.ymlに定義されていれば問題ありません)

下記はDockerボリュームを作成します。

  • mysqlのdatadir
  • rubyのgem_home
  • nodeのnode_modules
docker-compose.yml
version: '3'
services:
  db:
    image: mysql:5.7.27
    restart: always
    volumes:
      - db-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
  backend:
    build: ./backend
    ports:
      - ${BACKEND_PORT}:3000
    command: /bin/sh -c "rm -f /app/tmp/pids/server.pid && bundle exec rails s -b ${BACKEND_HOST}"
    volumes:
      - ./backend:/app
      - backend-bundle:/usr/local/bundle
    environment:
      - HOST=${BACKEND_HOST}
      - PORT=${BACKEND_PORT}
    depends_on:
      - db
    tty: true
    stdin_open: true
  frontend:
    build: ./frontend
    ports:
      - ${FRONTEND_PORT}:8080
    command: /bin/sh -c "yarn dev"
    volumes:
      - ./frontend:/app
      - frontend-node_modules:/app/node_modules
    environment:
      - HOST=${FRONTEND_HOST}
      - PORT=${FRONTEND_PORT}
    tty: true
volumes:
  db-data:
  backend-bundle:
  frontend-node_modules:

backend

Rails環境構築のためのファイルを準備します。

./backend/Gemfile

Railsのバージョンのみ指定しておきます。

Gemfile
source 'https://rubygems.org'

gem 'rails', '5.2.3'

./backend/Gemfile.lock

空ファイルをtouchしておけばOKです。

./backend/Dockerfile

Alpine Linuxのパッケージは最低限のものだけインストールします。
開発を進めるうちにgemのインストールで依存エラーが発生した場合には、不足パッケージを都度追加しましょう。

Dockerfile
FROM ruby:2.6.3-alpine

ENV RUNTIME_PACKAGES "mysql-client mysql-dev tzdata nodejs"
ENV DEV_PACKAGES "build-base curl-dev"
ENV APP_HOME /app
ENV TZ Asia/Tokyo

ENV HOST 0.0.0.0
ENV PORT 3000

WORKDIR ${APP_HOME}
ADD Gemfile ${APP_HOME}/Gemfile
ADD Gemfile.lock ${APP_HOME}/Gemfile.lock

RUN apk update \
    && apk upgrade \
    && apk add --update --no-cache ${RUNTIME_PACKAGES} \
    && apk add --update --no-cache --virtual=.build-dependencies ${DEV_PACKAGES} \
    && bundle install -j4 \
    && rm -rf /usr/local/bundle/cache/*.gem \
        && find /usr/local/bundle/gems/ -name "*.c" -delete \
        && find /usr/local/bundle/gems/ -name "*.o" -delete \
    && apk del --purge .build-dependencies \
    && rm -rf /var/cache/apk/*

COPY . ${APP_HOME}

EXPOSE ${PORT}

CMD ["rails", "server", "-b", ${HOST}]

frontend

./frontend/Dockerfile

Dockerfile
FROM node:12.9.0-alpine

ENV APP_HOME /app
ENV PATH ${APP_HOME}/node_modules/.bin:$PATH
ENV TZ Asia/Tokyo

ENV HOST 0.0.0.0
ENV PORT 8080

WORKDIR ${APP_HOME}
ADD . ${APP_HOME}

RUN apk update \
    && apk upgrade \
    && yarn install \
    && rm -rf /var/cache/apk/*

EXPOSE ${PORT}

CMD ["yarn", "dev"]

アプリケーション作成

Rails、Nuxt環境にプロジェクトを作成します。
docker-compose run を実行したタイミングで、それぞれのDockerイメージがbuildされ、さらにコンテナが立ち上がります。
--no-deps ... docker-compose.ymlで depends_on or links 指定するサービスは起動しない。
--rm ... 処理を終えたコンテナを自動的に削除。

backend

アプリケーション作成

rails new でRailsアプリケーションを作成します。
--api オプションでAPIモードにしていますが、不要な方は外してください。

$ docker-compose run --no-deps --rm backend rails new . --force --api --database=mysql --skip-bundle

DB接続のため、下記のファイルを修正します。

.
└ backend
    ├ config
    │  └ database.yml
    ├ Gemfile
    └ .env

./backend/.env

docker-compose.ymlで参照している MYSQL_ROOT_PASSWORD と同じもの設定します。

MYSQL_ROOT_PASSWORD=password

./backend/Gemfile

.envから環境変数を読み込むdotenv-railsというgemを追加します。

Gemfile
~~省略~~

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]

  gem "dotenv-rails" #追加
end

~~省略~~

./backend/config/database.yml

DBへのアクセスに使用するパスワードを、環境変数から取得します。

database.yml
~~省略~~

default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: <%= ENV.fetch("MYSQL_ROOT_PASSWORD") { '' } %> #環境変数を参照するように修正
  host: db #localhostからdocker-compose.ymlのサービス名に修正

~~省略~~

frontend

アプリケーション作成

npx でNuxtアプリケーションを作成します。

後から追加/変更できるので、このタイミングではEnterキー連打でOKです。

$ docker-compose run --rm frontend npx create-nuxt-app .

~~省略~~

create-nuxt-app v2.10.0
✨  Generating Nuxt.js project in .
? Project name #アプリ名 <Enter>
? Project description #任意 <Enter>
? Author name #任意 <Enter>
? Choose the package manager #Yarn <Enter>
? Choose UI framework #None <Enter>
? Choose custom server framework #None <Enter>
? Choose Nuxt.js modules #(Nothing) <Enter>
? Choose linting tools #(Nothing) <Enter>
? Choose test framework #None <Enter>
? Choose rendering mode #Universal (SSR) <Enter>
? Choose development tools #(Nothing) <Enter>

Dockerイメージ作成

アプリケーション作成時にできたDockerボリュームは削除しておきます。

$ docker-compose down --volume
# もしくは docker volume rm ボリューム名

各ファイルを修正した状態で、Dockerイメージをビルドします。

$ docker-compose build

docker-compose.ymlで build を指定しているbackendとfrontendのイメージが作成されたことを確認します。

$ docker images --format "{{.Repository}}\t{{.CreatedSince}}" ${NEW_APP}*
rails-nuxt-app_frontend     About a minutes ago
rails-nuxt-app_backend      About a minutes ago

Hello World

最後にDockerコンテナを起動し、Rails、NuxtアプリケーションのHelloWorldを確認します、

docker-compose.ymlで定義したサービスを -d オプション(デタッチモード)でバックグラウンド起動します。

$ docker-compose up -d

プロセスが立ち上がっていることを確認します。

$ docker-compose ps
        Name                       Command               State           Ports         
---------------------------------------------------------------------------------------
rails-nuxt-app_backend_1    /bin/sh -c rm -f /app/tmp/ ...   Up      0.0.0.0:3000->3000/tcp
rails-nuxt-app_db_1         docker-entrypoint.sh mysqld      Up      3306/tcp, 33060/tcp   
rails-nuxt-app_frontend_1   docker-entrypoint.sh /bin/ ...   Up      0.0.0.0:8080->8080/tcp

失敗している場合は docker-compose logs などで原因を探りましょう。

backend

RailsアプリケーションのDBを作成します。

$ docker-compose exec backend rails db:create
Created database 'app_development'
Created database 'app_test'

ブラウザで http://localhost:3000/ を開きます。
rails-helloworld.png

frontend

ブラウザで http://localhost:8080/ を開きます。
nuxt-helloworld.png


お疲れさまでした。
次回は GraphQL の導入について投稿したいと思います。

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

Vue.jsとCSSアニメーションでラーメンタイマーを作る + Firebaseで簡単公開

学びたてだけどVue.js + CSSアニメーション + 絵を組み合わせて作ってみたい!
という事でシンプルなカップラーメンのタイマーを作ってみました。

基本的なVue.jsで始めたばかりの方でも比較的読み進めやすいと思います。
話題のFirebaseで無料公開もとても簡単だったので方法をまとめました。

? ラーメンタイマー

・アプリURL
https://cupramen-timer.firebaseapp.com/
・GitHub - ソースコード
https://github.com/aocattleya/Ramen_Timer

(いいねやスター貰えるととても喜びます!)
 

アプリの内容

使用している物 説明
HTML, CSS animationプロパティを使用
Vue.js JavaScriptフレームワーク
Firebase 無料枠でアプリを公開する
--- ---
SweetAlert アラートを簡単にデザインするライブラリ
GoogleFonts フォントの変更
FireAlpaca ペイントツール(無料)

 
3分 or 5分のボタンをホバーするとキャラクターが左右に振り向きます。
時間を選びクリックするとカウントダウンタイマーが使える簡単な内容です。
レスポンシブデザイン(スマホ)にも対応しています。※左右の振り向きはしない

キャラクターデザイン

まず一番目立つキャラクター
自分は元々漫画家のアシスタントをしていた時期などがあり、
描いた物をアプリに組み合わせてみたかったので描き描き。

レイヤーといって何枚もの板が重なっているように描かれています。
なので例えば手を消すと下に隠れている髪が出てきます。
これによって一枚描いたら背景などが簡単に変更でき、左右反転させて手を描けば2枚完成。

※猫の方が万人受けする。名前募集中!

こちらに原寸サイズ画像&レイヤーファイルを公開しています。
illustration-Original_Characters(GitHub)
 

? コードの解説

それぞれ分けて解説していきます。
コードは分かりやすいように関係ない部分を大幅に省略して書いてます。

 

・キャラクターを左右に振り向かせる

1行目、v-bind:class=""によってclassを「右顔CSS」⇔「左顔CSS」と書き替えており、
きっかけは、それぞれの3分と5分ボタンにあるv-on:mouseover=""

index.html
<!-- キャラクター -->
<img v-bind:class="{ right_face: isRight, left_face: isLeft }" />
  <div class="pick-button">
    <!-- 3分ボタン -->
    <input type="image" src="img/3min.png" v-on:mouseover="rotate_left" />
    <!-- 5分ボタン -->
    <input type="image" src="img/5min.png" v-on:mouseover="rotate_right" />
  </div>
</div>

 
JavaScript側では下記のようにボタンをホバーした時に、
「true」⇔「false」で切り替わる処理を書いています。

main.js
const vm = new Vue({
  el: "#app",
  data: {
    isLeft: false,
    isRight: true,
  },
  methods: {
    rotate_left: function() {
      this.isLeft = true;
      this.isRight = false;
    },
    rotate_right: function() {
      this.isLeft = false;
      this.isRight = true;
    }
  }
});

 
右顔のCSSアニメーション

style.css
.right_face {
  border-radius: 50%;
  background-image: url(../img/button_right.png);
  animation: anime 0.4s linear 2;
}
@keyframes anime {
  100% {
    transform: rotateY(360deg);
  }
}

border-radius: 50%で画像を丸くする。
・そしてanimationプロパティ、長さなどを設定でき@keyframesで動きを付けます。
 上記は100%終了時までにY軸に360度回転としています。
 
 
 

・ボタン選択で画面の切り替え

ページは変わっていません。
Vue.jsで最初の画面を非表示にし、非表示になっていたタイマー画面を表示させています。

3分 or 5分ボタンを押すとv-on:click="show = !show"によって、
<div v-show="!show"><div v-show="show">と画面表示が切り替わります。

index.html
<!-- 3分 or 5分ボタン選択画面 -->
<div v-show="!show">
  <input type="image" src="img/3min.png" v-on:click="show = !show" />
  <input type="image" src="img/5min.png" v-on:click="show = !show" />
</div>

<!-- タイマー画面 -->
<div v-show="show">
  <span id="minutes">{{ minutes }}</span>
  <span id="middle">:</span>
  <span id="seconds">{{ seconds }}</span>
  <!-- 省略 -->
</div>

 
 

・タイマーの実装

timre3.gif

index.html
<!-- 3分 or 5分ボタン -->
<input type="image" src="img/3min.png" v-on:click="show = !show; threeMin()" />
<input type="image" src="img/5min.png" v-on:click="show = !show; fiveMin()" />

3分 or 5分ボタンをクリックした時に、
v-on:click="threeMin()"又はv-on:click="fiveMin()"が実行され、
タイマー画面でカウントする時間を3:00 or 5:00にします。
 

index.html
<!-- タイマー -->
<div class="timer">
  <span id="minutes">{{ minutes }}</span>
  <span id="middle">:</span>
  <span id="seconds">{{ seconds }}</span>
</div>
<div id="buttons">
  <!-- スタート -->
  <input type="image" src="img/start.png" v-on:click="startTimer" v-if="!timer" />
  <!-- ストップ -->
  <input type="image" src="img/stop.png" v-on:click="stopTimer" v-if="timer" />
  <!-- リセット -->
  <input type="image" src="img/reset.png" v-on:click="resetTimer" v-if="resetButton" />
</div>

タイマーのカウントは、HTMLの{{ minutes }} : {{ seconds }}
この部分がVue.jsの処理でタイマーのように変更されていきます。
 

main.js
const vm = new Vue({
  el: "#app",
  data: {
    timer: null,
    pickTime: null,
    totalTime: null,
    resetButton: false
  },
  methods: {
    // 3分 or 5分ボタン
    threeMin: function() {
      this.pickTime = 3 * 60;
      this.totalTime = this.pickTime;
    },
    fiveMin: function() {
      this.pickTime = 5 * 60;
      this.totalTime = this.pickTime;
    },
    // スタート
    startTimer: function() {
      this.timer = setInterval(() => this.countdown(), 1000);
      this.resetButton = true;
    },
    // ストップ
    stopTimer: function() {
      clearInterval(this.timer);
      this.timer = null;
      this.resetButton = true;
    },
    // リセット
    resetTimer: function() {
      this.totalTime = this.pickTime;
      clearInterval(this.timer);
      this.timer = null;
      this.resetButton = false;
    },
    // 秒が一桁の場合0を追加
    padTime: function(time) {
      return (time < 10 ? "0" : "") + time;
    },
    // カウントダウン
    countdown: function() {
      if (this.totalTime >= 1) {
        this.totalTime--;
      } else {
        this.totalTime = 0;
        this.resetTimer();
        swal("Complete!!", "", "success");
      }
    }
  },
  computed: {
    // タイマーの数値
    minutes: function() {
      const minutes = Math.floor(this.totalTime / 60);
      return minutes;
    },
    seconds: function() {
      const seconds = this.totalTime - this.minutes * 60;
      return this.padTime(seconds);
    }
  }
});

computed:の部分がHTMLのタイマー{{ }}に表示され、ボタンで各methods:が働き、
スタートを押すとsetInterval()により、一秒ごとにcountdownが実行されます。
 
 
 

・アラートデザイン『SweetAlert』

sweet.png

簡単にアラートがデザイン出来るライブラリ
ブラウザからCDN経由で読み込みができます。

index.html
<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
main.js
swal("Complete!!", "", "success");

これだけ、凄い!
このアラートはスマホでも同じデザインで表示されます。

その他の使い方は下記のページが分かりやすいです。
- SweetAlertバージョン2を使ったデモ
 
 
 

? Firebaseで公開(無料)

Firebase - Google
 
最近とても話題のFirebaseで、作成したアプリを無料で公開。
データベースなど複雑に使う事も可能ですが、今回は簡単に無料で公開してみます。

下記はQiita用にtestプロジェクトで進めていますが、任意の名前にしてください。

1、ログイン
Firebaseのページを開いてログイン

2、プロジェクトの作成

プロジェクト名を記入します。
IDの部分はURLになりますので好みのIDに書き変えましょう。

Googleアナリティクスの設定を聞かれます。
今回は、使用しないで進めます。

3、フォルダの設定

Desktopにプロジェクト名と同じフォルダを作成します。
中にpublicという名前でフォルダを作成して公開したいコードを入れます。

4、Node.jsのインストール
まだ入ってない方はインストールしてください。
すぐ終わります。

Node.js

5、firebase-toolsをインストール

npm install -g firebase-tools

6、ログイン

firebase login

利用状況のデータを送っても良いか聞かれたら任意でY/n
次にGoogleログイン画面が表示されるので、アカウントを選び進めます。

7、デプロイ設定

cd Desktop/test

プロジェクトのフォルダに移動します。

firebase init

下記のような画面が表示されます。

◉ Hosting: Configure and deploy Firebase Hosting sites

Hostingをスペースで選択してからEnterを押します。
※スペースで選択していなとエラーとなる。

? Please select an option: 

Don't set up a default projectを選択してEnter

? What do you want to use as your public directory?(public)

そのままEnter(デフォルトのpublicとなる)

? Configure as a single-page app (rewrite all urls to /index.html)? (y/N) 

そのままEnter(デフォルトでN)

? File public/index.html already exists. Overwrite? (y/N) 

index.htmlを上書きしていいか聞いてくるのでN(そのままEnterでN)


上手くいきました。

8、公開する

firebase use --add

上記でEnter

? Which project do you want to add?
> test-71a0e

作成したプロジェクトを選択します。

? What alias do you want to use for this project? (e.g. staging)

名前は任意です。今回はstagingとしました。

firebase deploy

完了です!

9、Hosting URL:をブラウザでアクセス

こうして完成、簡単にFirebaseでの公開も出来ました。使ってね!

・ラーメンタイマー
https://cupramen-timer.firebaseapp.com/
・ソースコード - GitHub
https://github.com/aocattleya/Ramen_Timer

 
 

終わりに

Vue.jsで何か作ってみたい、CSSアニメーションと絵も組み合わせたい!

そんな考えから作ってみたアプリでシンプルに見えて苦戦もありました。
しかし言語を学習してるだけと違い、新たな発見と理解はとても多かったです。
基本的な構文でここまで作れて達成感も大きかったのでやって凄く良かったと思います。

次はさらにレベルを上げて作ってみたいですね!
最後まで読んでくれてありがとうございました。
 

参考

【CSS3】@keyframes と animation 関連のまとめ
SweetAlertバージョン2を使ったデモ
Firebaseを用いて5分でセキュアなWebサイトを公開する

リンク

:octocat: GitHub
https://github.com/aocattleya
? Twitter
https://twitter.com/aocattleya
? Qiita
https://qiita.com/aocattleya

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

Vue.jsとCSSアニメーションでラーメンタイマーを作った + Firebaseで簡単公開

Vue.jsを学び始めてCSSアニメーションも触ってみたいという事で簡単なアプリを作ってみました。
基本的なVue.jsの機能で始めたばかりの方でも比較的に読み進めやすいかと思います。
話題のFirebaseでの無料公開もとても簡単なので方法をまとめました。

? ラーメンタイマー

timre1.gif
・アプリURL
https://cupramen-timer.firebaseapp.com/
・GitHub - ソースコード
https://github.com/aocattleya/Ramen_Timer

(いいねやスター貰えるととても喜びます!)
 

アプリの内容

使用している物 説明
HTML, CSS animationプロパティを使用
Vue.js JavaScriptフレームワーク
Firebase 無料枠でアプリを公開する
--- ---
SweetAlert アラートを簡単にデザインするライブラリ
GoogleFonts フォントの変更
FireAlpaca ペイントツール(無料)

 
3分 or 5分のボタンをホバーするとキャラクターが左右に振り向きます。
時間を選びクリックするとカウントダウンタイマーが使える簡単な内容です。
レスポンシブデザイン(スマホ)にも対応しています。※左右の振り向きはしない

キャラクターデザイン

まず一番目立つキャラクター
自分は元々漫画家のアシスタントをしていた時期などがあり、
描いた物をアプリに組み合わせてみたかったので描き描き。

レイヤーといって何枚もの板が重なっている感じで描かれています。
なので例えば手を消すと下に隠れている髪が出てきます。
これによって一枚描いたら背景などが簡単に変更でき、左右反転させて手を描けば2枚完成。

※猫の方が万人受けする。名前募集中!

こちらに原寸サイズ画像&レイヤーファイルを公開しています。
illustration-Original_Characters(GitHub)
 

? コードの解説

それぞれ分けて解説していきます。
コードは分かりやすいように関係ない部分を大幅に省略して書いてます。

 

・キャラクターを左右に振り向かせる

1行目、v-bind:class=""によってclassを「右顔CSS」⇔「左顔CSS」と書き替えており、
きっかけは、それぞれの3分と5分ボタンにあるv-on:mouseover=""

index.html
<!-- キャラクター -->
<img v-bind:class="{ right_face: isRight, left_face: isLeft }" />
  <div class="pick-button">
    <!-- 3分ボタン -->
    <input type="image" src="img/3min.png" v-on:mouseover="rotate_left" />
    <!-- 5分ボタン -->
    <input type="image" src="img/5min.png" v-on:mouseover="rotate_right" />
  </div>
</div>

 
JavaScript側では下記のようにボタンをホバーした時に、
「true」⇔「false」で切り替わる処理を書いています。

main.js
const vm = new Vue({
  el: "#app",
  data: {
    isLeft: false,
    isRight: true,
  },
  methods: {
    rotate_left: function() {
      this.isLeft = true;
      this.isRight = false;
    },
    rotate_right: function() {
      this.isLeft = false;
      this.isRight = true;
    }
  }
});

 
右顔のCSSアニメーション

style.css
.right_face {
  border-radius: 50%;
  background-image: url(../img/button_right.png);
  animation: anime 0.4s linear 2;
}
@keyframes anime {
  100% {
    transform: rotateY(360deg);
  }
}

border-radius: 50%で画像を丸くする。
・そしてanimationプロパティ、長さなどを設定でき@keyframesで動きを付けます。
 上記は100%終了時までにY軸に360度回転としています。
 
 
 

・ボタン選択で画面の切り替え

ページは変わっていません。
Vue.jsで最初の画面を非表示にし、非表示になっていたタイマー画面を表示させています。

3分 or 5分ボタンを押すとv-on:click="show = !show"によって、
<div v-show="!show"><div v-show="show">と画面表示が切り替わります。

index.html
<!-- 3分 or 5分ボタン選択画面 -->
<div v-show="!show">
  <input type="image" src="img/3min.png" v-on:click="show = !show" />
  <input type="image" src="img/5min.png" v-on:click="show = !show" />
</div>

<!-- タイマー画面 -->
<div v-show="show">
  <span id="minutes">{{ minutes }}</span>
  <span id="middle">:</span>
  <span id="seconds">{{ seconds }}</span>
  <!-- 省略 -->
</div>

 
 

・タイマーの実装

timre3.gif

index.html
<!-- 3分 or 5分ボタン -->
<input type="image" src="img/3min.png" v-on:click="show = !show; threeMin()" />
<input type="image" src="img/5min.png" v-on:click="show = !show; fiveMin()" />

3分 or 5分ボタンをクリックした時に、
v-on:click="threeMin()"又はv-on:click="fiveMin()"が実行され、
タイマー画面でカウントする時間を3:00 or 5:00にします。
 

index.html
<!-- タイマー -->
<div class="timer">
  <span id="minutes">{{ minutes }}</span>
  <span id="middle">:</span>
  <span id="seconds">{{ seconds }}</span>
</div>
<div id="buttons">
  <!-- スタート -->
  <input type="image" src="img/start.png" v-on:click="startTimer" v-if="!timer" />
  <!-- ストップ -->
  <input type="image" src="img/stop.png" v-on:click="stopTimer" v-if="timer" />
  <!-- リセット -->
  <input type="image" src="img/reset.png" v-on:click="resetTimer" v-if="resetButton" />
</div>

タイマーのカウントは、HTMLの{{ minutes }} : {{ seconds }}
この部分がVue.jsの処理でタイマーのように変更されていきます。
 

main.js
const vm = new Vue({
  el: "#app",
  data: {
    timer: null,
    pickTime: null,
    totalTime: null,
    resetButton: false
  },
  methods: {
    // 3分 or 5分ボタン
    threeMin: function() {
      this.pickTime = 3 * 60;
      this.totalTime = this.pickTime;
    },
    fiveMin: function() {
      this.pickTime = 5 * 60;
      this.totalTime = this.pickTime;
    },
    // スタート
    startTimer: function() {
      this.timer = setInterval(() => this.countdown(), 1000);
      this.resetButton = true;
    },
    // ストップ
    stopTimer: function() {
      clearInterval(this.timer);
      this.timer = null;
      this.resetButton = true;
    },
    // リセット
    resetTimer: function() {
      this.totalTime = this.pickTime;
      clearInterval(this.timer);
      this.timer = null;
      this.resetButton = false;
    },
    // 秒が一桁の場合0を追加
    padTime: function(time) {
      return (time < 10 ? "0" : "") + time;
    },
    // カウントダウン
    countdown: function() {
      if (this.totalTime >= 1) {
        this.totalTime--;
      } else {
        this.totalTime = 0;
        this.resetTimer();
        swal("Complete!!", "", "success");
      }
    }
  },
  computed: {
    // タイマーの数値
    minutes: function() {
      const minutes = Math.floor(this.totalTime / 60);
      return minutes;
    },
    seconds: function() {
      const seconds = this.totalTime - this.minutes * 60;
      return this.padTime(seconds);
    }
  }
});

computed:の部分がHTMLのタイマー{{ }}に表示され、ボタンで各methods:が働き、
スタートを押すとsetInterval()により、一秒ごとにcountdownが実行されます。
 
 
 

・アラートデザイン『SweetAlert』

sweet.png

簡単にアラートがデザイン出来るライブラリ
ブラウザからCDN経由で読み込みができます。

index.html
<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
main.js
swal("Complete!!", "", "success");

これだけ、凄い!
このアラートはスマホでも同じデザインで表示されます。

その他の使い方は下記のページが分かりやすいです。
- SweetAlertバージョン2を使ったデモ
 
 
 

? Firebaseで公開(無料)

Firebase - Google
 
最近とても話題のFirebaseで、作成したアプリを無料で公開。
データベースなど複雑に使う事も可能ですが、今回は簡単に無料で公開してみます。

下記はQiita用にtestプロジェクトで進めていますが、任意の名前にしてください。

1、ログイン
Firebaseのページを開いてログイン

2、プロジェクトの作成

プロジェクト名を記入します。
IDの部分はURLになりますので好みのIDに書き変えましょう。

Googleアナリティクスの設定を聞かれます。
今回は、使用しないで進めます。

3、フォルダの設定

Desktopにプロジェクト名と同じフォルダを作成します。
中にpublicという名前でフォルダを作成して公開したいコードを入れます。

4、Node.jsのインストール
まだ入ってない方はインストールしてください。
すぐ終わります。

Node.js

5、firebase-toolsをインストール

npm install -g firebase-tools

6、ログイン

firebase login

利用状況のデータを送っても良いか聞かれたら任意でY/n
次にGoogleログイン画面が表示されるので、アカウントを選び進めます。

6、デプロイ設定

cd Desktop/test

プロジェクトのフォルダに移動します。

firebase init

下記のような画面が表示されます。

◉ Hosting: Configure and deploy Firebase Hosting sites

Hostingをスペースで選択してからEnterを押します。
※スペースで選択していなとエラーとなる。

? Please select an option: 

Don't set up a default projectを選択してEnter

? What do you want to use as your public directory?(public)

そのままEnter(デフォルトのpublicとなる)

? Configure as a single-page app (rewrite all urls to /index.html)? (y/N) 

そのままEnter(デフォルトでN)

? File public/index.html already exists. Overwrite? (y/N) 

index.htmlを上書きしていいか聞いてくるのでN(そのままEnterでN)


上手くいきました。

・公開する

firebase use --add

上記でEnter

? Which project do you want to add?
> test-71a0e

作成したプロジェクトを選択します。

? What alias do you want to use for this project? (e.g. staging)

名前は任意です。今回はstagingとしました。

firebase deploy

完了です!

Hosting URL:をブラウザでアクセス

こうして完成、簡単にFirebaseでの公開も出来ました。使ってね!

・ラーメンタイマー
https://cupramen-timer.firebaseapp.com/
・ソースコード - GitHub
https://github.com/aocattleya/Ramen_Timer

 
 

終わりに

まだVue.js学びたてだけど何か作ってみたい
CSSアニメーションも使って絵も描いて組み合わせたい!

そんな考えから簡単に作ってみたアプリでシンプルに見えて苦戦もありました。
しかし言語を学習してるだけと違い、新たな発見と理解はとても多かったです。
基本的な構文でここまで作れて達成感も大きかったので凄くやって良かったと思います。

次はさらにレベルを上げて作ってみたいですね!
最後まで読んでくれてありがとうございました。
 

参考

【CSS3】@keyframes と animation 関連のまとめ
SweetAlertバージョン2を使ったデモ
Firebaseを用いて5分でセキュアなWebサイトを公開する

リンク

:octocat: GitHub
https://github.com/aocattleya
? Twitter
https://twitter.com/aocattleya
? Qiita
https://qiita.com/aocattleya

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

Vue.jsのvue.config.jsを使って環境変数を別けるやり方

developmentとproductionで分けたかったので自分用にメモです

STEP1

スクリーンショット 2019-08-27 18.15.15.png
こんな感じででpackage.jsonのコマンドにmodeを追加する。
npm run p とやるとデフォルトで --mode production となります。

STEP2

スクリーンショット 2019-08-27 18.09.46.png
プロジェクトの直下に
.env.development
.env.production
といったファイルを作る。

STEP3

スクリーンショット 2019-08-27 18.10.13.png
中身を書く。
この時命名のルールがあり
VUE_APP_hogehogehoge としなければならない。(でないとundefinedとなってしまいます。)

STEP4

スクリーンショット 2019-08-27 18.10.32.png
適当なところで呼び出して確認する。
終わり。

npm run xxxx でもう一度立ち上げなおす。


ちなみに話は全然変わるけどSourceマップの出し分けはこうする

module.exports = {
    productionSourceMap: process.env.NODE_ENV === 'production'? false :true,
}
// この時の process.env.NODE_ENV は package.jsonのコマンドの --mode xxxxxx を自動的に読み込んでくれる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vue.js テキストエリア伸ばす

テキストエリアを文字数に応じて自動で伸ばす。
ちゃんとコンポーネントも読み出す。

今回は element ui を使う。
一般的なテキストエリアとは違うので注意。

top.vue
<el-form-item label="無料占いの内容" prop="message">
    <TextareaResizeComponent
            v-model="form.message"
            placeholder="ここにサンプル"
            name="message"
    ></TextareaResizeComponent>
    <!--<el-input type="textarea" v-model="form.message" :rows=5></el-input>-->
</el-form-item>


・・・略
<script>
    import TextareaResizeComponent from "./TextareaResizeComponent";

    export default {
        components: {TextareaResizeComponent},
        data() {
                form: {
                    message:''
                },
</script>

テキストエリアリサイズコンポーネント

TextareaResizeComponent.vue
<template>

<el-input
        :name="name"
        :value="value"
        :placeholder="placeholder"
        :rows="rows"
        type="textarea"
        @input="updateValue"
></el-input>


</template>

<script>
    export default {
        name: "TextareaResizeComponent",
        props: {
            value: { type: String, require: true },
            name: { type: String, require: true },
            placeholder: { type: String, require: false },
            cols: { type: Number, require: false }
        },
        computed: {
            rows: function() {
                var num = this.value.split('\n').length;
                return (num > 10) ? num : 10;
            }
        },
        methods: {
            updateValue: function(e) {
                //普通のテキストエリアと違うので注意
                this.$emit("input", e);
            }
        }
    };
</script>

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

次世代キーボード Tap Strap を買ったので使い方を練習するサイトを作った

先日、何かの拍子に Tap Strap というキーボードを知りました。
メリケンサックのように指にはめるタイプのキーボードです。

https://www.tapwithus.com/

キーボードを指にはめて、ピアノを引くときのように決められた組み合わせで指を軽くタップすると、文字が打てるというものです。
このキーボード、片手だけで文字や数字を打ち込めるのでとても便利なのですが、指の使い方を覚えるのが大変です。
アルファベットの指の使い方はこんな感じ。

o がタップする指、 x がタップしない指です。

{
  a: 'oxxxx', b: 'xoxxo', c: 'oxooo', d: 'oxoxx', e: 'xoxxx', f: 'ooxox',
  g: 'oxoox', h: 'xoooo', i: 'xxoxx', j: 'oooxo', k: 'oxxox', l: 'xxoox',
  m: 'xoxox', n: 'ooxxx', o: 'xxxox', p: 'ooxxo', q: 'xooxo', r: 'oooox',
  s: 'xxxoo', t: 'xooxx', u: 'xxxxo', v: 'ooxoo', w: 'oxoxo', x: 'xoxoo',
  y: 'oxxxo', z: 'xxoxo'//, bs: 'xooox', enter: 'oxxoo'
}

なかなか複雑です。。。
公式でもその問題は認識しているらしく、Tap Academyという練習アプリがリリースされています。
が。初めて使う人にとっては難易度が高い。。

ということで、自分で作ってみました。
見た目は残念な感じですが、一応ちゃんと動きます。

レポジトリ
https://github.com/gorogoroyasu/tap-strap-practice
作ったサイト
https://gorogoroyasu.github.io/tap-strap-practice/single/

見た目

https___gorogoroyasu_github_io_tap-strap-practice_single_.png

間違えたとき

https___gorogoroyasu_github_io_tap-strap-practice_single_.png

3回間違えたらヒントを出す!

https___gorogoroyasu_github_io_tap-strap-practice_single_.png

機能としては、上述した程度のものしかありません。
今の所、
1. Question を出す。
2. 正解だったら次の問題へ。間違いだったら、何と何を間違えたのかを出す
3. 3回以上間違えたら hint と称した答えを出す
という感じです。

あと、割とえげつないのが、問題が無限ループしていること。
この辺は、ちょこちょこいい感じに直していこうと思います。

Vue.js

殆ど使ったことはないですが、使ってみました。
難しかったのは、 form にフォーカスを当てるところでした。
ドキュメントに書いてたんですけどね。。
https://jp.vuejs.org/v2/guide/custom-directive.html

directives: {
    focus: {
      inserted: function (el) {
      el.focus()
    }
  }
},

そもそも ディレクティブ という機能を知らなかったので勉強になりました。

あと、body タグにid を振って

new Vue({
  el: '#body',
  data: obj
})

という風にしようとしていたのですが、だめだよって怒られました。
苦肉の策で、

<body>
  <div id='body'>
  </div>
</body>

という構成にして、逃げました。

たぶん、そもそもの使い方が違うんだと思います。
時間を見つけて正しい書き方に修正しようと思います。
あと、見た目がだいぶ悪いので、それもなんとかしようと思います。

最後に

$200 と決して安くはないですが、もし購入される方がいらっしゃったら、このクーポンを使っていただけると。
10% off になるらしいです。

Enjoy Tap Strap!

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

propsとdataの違い、propsについて

TL;DR

他の使い方もあるだろうけど、とりあえず大体以下じゃね?って解釈

  • props
    • 親コンポーネントから渡されるであろう値を自コンポーネントに定義しておくやつ
  • data
    • storeから取得されるデータの形など(を定義しておこう)

はじめに

vueの例は単一ファイルコンポーネントで記入します
また、propsとdataの比較記事かと思いきやほぼpropsの事しか話していません。
dataについては多分別記事で書くよ
多分

propsについて

コンポーネントの基本 #プロパティを使用した子コンポーネントへのデータの受け渡し

親コンポーネントから子コンポーネントに値を渡す際に使用されるのがpropsです。

親コンポーネント

<Content title='`ほげ`' />

子コンポーネント

<template>
  <div>{{ title }}</div>
</template>

export default {
  name: 'Content',
  props: ['title']
}

注意1. propsの形式

上ではわかりやすさのためにただの文字列として渡しましたが、本当は以下の様にどの様な値が来るかまで書いておく方が好ましいです。
(Props should at least define its types)

子コンポーネント

export default {
  name: 'Content',
  props: {
    title: {
      type: String,
      required: true
    }
  }
}

プロパティ #プロパティの型

注意2. propsをHTMLの内容に渡す時と属性に渡す時

HTMLの内容に渡す時

<template>
  <div>{{ title }}</div>
</template>

子コンポーネント側で先ほどの様に{{}}波括弧を2つ書いたもの(”Mustache” 構文(二重中括弧))でくくればOK

HTMLの属性に渡す時

<template>
  <div :class="title">ここはベタ書き</div>
</template>

子コンポーネント側でv-bindを使用することで渡されたprops要素が入り込む様になっている

注意3. 親コンポーネントでの渡し方について

ベタ書きの時(あんまないと思うけど)

<Content title='`ほげ`' />

親コンポーネント側でバッククオートで囲いながらベタ書きすればOK(当たり前のことを言っている人)
文字列として判断させたいため、バッククオートで囲う

dataとかから取得する場合

v-forと交えながら書きます(コンポーネントに渡す時data使うなら大体v-for使うだろという仮定)

<template>
...
<Content 
  v-for="item in items"
  :key="item.id" :id="item.id"
  :content="item.content"
  :user-id="item.userId"
/>
</template>

export default {
  data: function() {
    items: [
       {
          id: 1,
          content: 'ぴよぴよ',
          userId: 3
       },
       {
          id: 2,
          content: 'わんわん',
          userId: 4
       },
    ]
  }
}

親コンポーネント側でこの様な記述をします。注意点は以下。

dataについて

そのコンポーネントで使用するデータ(子コンポーネントに渡すものも含む)を定義しておく場所
多分大体storeとかから取得したデータが入ってる場所と考えるべき?(computedなどと併用して)

注意点1. コンポーネントでのdataは関数にする

コンポーネントの基本 #dataは関数でなければいけません

コンポーネントじゃなかったらオブジェクトでよかったですよね
Vue インスタンス #データとメソッド


最後に

mapStateを使ってstoreからデータを取得してほにゃらららへんはちょっとまだ勉強中なので、理解したら記事を書きます

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