- 投稿日:2021-03-06T21:36:46+09:00
Vuex moduleのAction内でエラーを投げたら怒られた
背景
vuex-module-decorators
を使用し、Actionを宣言しました。AuthStore.ts@Action public async addUser(params: loginParams) { // API呼び出し await ApiService.addUser(params.email, params.password); // 以下省略 }それを、APIからエラーが返るような呼び出し方をすると、
このようなエラーが出ました。Error: ERR_ACTION_ACCESS_UNDEFINED: Are you trying to access this.someMutation() or this.someGetter inside an @Action? That works only in dynamic modules. If not dynamic use this.context.commit("mutationName", payload) and this.context.getters["getterName"]原因
vuex-module-decoratorでは、Action内で発生したオリジナルのエラーを投げることができる
rawError
という設定が、デフォルトでfalseとなっています。その為、Actions内でエラーを投げようとしても、
それが呼び出し元に届いていないということでした。対処
ActionにrawErrorを設定します
@Action{( rawError: true }) public async addUser(params: loginParams) { // API呼び出し await ApiService.addUser(params.email, params.password); // 以下省略 }もしくは、ファイルの頭で設定を行い、全体にrawErrorを設定してしまいます。
import { config } from 'vuex-module-decorators'; config.rawError = true;参考
- 投稿日:2021-03-06T19:40:50+09:00
Vuetify + Chart.jsで、グラフカードコンポーネントの作成
ダッシュボードを作成するのに、カード型のUIでグラフ表示をしてみたかったので、
VuetifyとChart.jsで簡単な実装をしてみました。対象読者
以下の内容を書いてます。
- Vuetifyのカードコンポーネント実装
- そこに、Chart.jsの埋め込み
- Nuxt.JSのレンダリングタイミングの制御
できたもの
準備
create-nuxt-app
でプロジェクトを作成します。
参考 インストール - NuxtJS$ yarn create nuxt-app <project-name>
もしくは、GitHubにプロジェクトソースを置いてるので、それを利用してください。
今回は、Vuetifyベースでの記述になります。Nuxt.JSでChart.jsを利用するために、Chart.jsと、vue-chart.jsをインストールします。
yarn add vue-chartjs chart.jsデータをベタガキ
まずは、Chart.jsの設定に直接値を書き込みます。
components/charts/chart-card.vue
グラフ部分作成
まず,Chart.jsのレンダリングをするコンポーネントを記述します。
グラフを描画したい時は、このコンポーネントを呼び出します。
components/charts/chart-card/linechart.vue
<script> import { Line } from 'vue-chartjs'; export default { extends: Line, name: 'chart', props: ['chartData', 'options'], mounted() { this.renderChart(this.chartData, this.options) } } </script>カード作成 + グラフ描画
components/charts/chart-card/chart-card.vue
Vuetifyの、
v-card
でカード部分を実現。
メインコンテンツを実装するv-card-text
に、グラフ部分のコンポーネントを配置します。template部
<template> <v-card min-width="80%"> <v-card-title> Simple Chart </v-card-title> <v-card-text class="chart-container"> <Chart :chartData="chartData" :options="options"/> </v-card-text> </v-card> </template>script部
data()で、グラフ描画に必要な情報を記述
chartData
- labels: X軸のラベル (Array)
- datasets
- label: 各線の凡例
- data: データの数値
- fill: 線グラフより下を塗りつぶすか
- backgroundColor: 塗りつぶす色
- borderColor: 線の色
options
- responsive: レスポンシブのOn/Off
- maintainAspectRatio: 縦横比を自動調整するか
- legend: 凡例の設定
<script> import Chart from './linechart.vue'; export default { components: { Chart }, data() { return { chartData: { labels: ['A', 'B', 'C'], datasets: [{ label: "sample", data: [100, 150, 300], fill: true, backgroundColor: 'rgba(83, 224, 156, 0.2)', borderColor: '#53e09c' }] }, options: { responsive: true, maintainAspectRatio: false, legend: { display: true }, } } }, } </script>これをpageで描画
作成した、カードコンポーネントをpageで描画します。
<template> <div> <v-row> <v-col cols="12" sm="12" md="6" lg="4"> <chartCard /> </v-col> </v-row> </div> </template> <script> import chartCard from '~/components/charts/chart-card/chart-card.vue'; export default { components: { chartCard, } } </script>Vue Storeからの取得
components/charts/chart-card-withStore.vue
ベタガキで使うタイミングなんてそんなにないので、
よくNuxtで用いるパターンとして、Vuexを使うパターンで実装します。ベタ描き部分との差分のみ記述します
Store (Vuex)を作成
store/chart.js
に、Chart用のStoreを作成。参考
* 【Vue.js】Vuexの使い方を解説【データ保存】 | CodeCampusexport const state = () => ({ simple: { label: ['AA', 'AB', 'AC'], data: [1000, 2000, 2500] }, })カード作成 + グラフ描画
this.$store.state...
で、NuxtのどこからでもStoreの値にアクセス可能なので、
chartDataの、labelsとdataの情報を、Storeから引っ張ってくるように記述。
+++
: 前の手順からの追記箇所... <script> import Chart from './linechart.vue'; export default { (...) data() { return { (...) chartData: { +++ labels: this.$store.state.charts_store.simple.label, datasets: [{ label: "sample", +++ data: this.$store.state.charts_store.simple.data, fill: true, backgroundColor: 'rgba(235, 70, 52, 0.2)', borderColor: '#eb4634', }] }, (...) } </script>pageに追記すると
+++
: 前の手順からの追記箇所
(...)
: 省略<template> <div> <v-row> (...) +++ <v-col cols="12" sm="12" md="6" lg="4"> +++ <storeChartCard /> +++ </v-col> </v-row> </div> </template> <script> (...) +++ import storeChartCard from '~/components/charts/chart-card/chart-card-withStore.vue'; export default { components: { chartCard, +++ storeChartCard, } } </script>これで、Storeの情報を使って、グラフ描画できました。
外部APIでの実行
components/charts/chart-card-withAPI.vue
Webアプリで一番使うのが、外部APIから情報を取得して、画面に描画する方法だと思います。なのでそのパターンを実装してみましょう。
今回は、日経平均株価のサンプルでやるために、Quandlで取得してみます。
RuntimeConfig | 外部APIのアクセストークン管理
APIを活用する時に、一番問題なのは、Nuxt.jsにToken情報などが晒されてしまう問題なので、今回は、Nuxt.jsのRuntimeConfigを使います、
かなり重要
本来は、privateRuntimeConfig
を使うのですが、SSR化が必要だったりと色々設定が必要なので、今回は簡単するために、publicRuntimeConfig
で実装します。ブラウザでAPIリクエストするので、アクセストークンは見えてしまう実装となります。完全に隠す必要がある場合は、SSR化して、privateRuntimeConfigで実装する必要があります。publicRuntimeConfigの設定
APIアクセストークンを管理するために以下の手順を取ります。
- 環境変数ファイル '.env'を作成
- .envファイルの中を
nuxt.config.js
から読み込み環境変数ファイル作成
Nuxt.jsにおいて、環境変数情報は、.envファイルで管理します。
QUANDL_KEY=<Quandlのアクセストークン>そして、
nuxt.config.js
に、process.env.変数名
で .envファイルの中見を参照できます。publicRuntimeConfig: { quandlKey: process.env.QUANDL_KEY | <トークンベタガキ> },利用方法
コンポーネント等から、
this.$config.quandlKey
とすることで、環境変数の値を参照することができます。カード作成 + グラフ作成
コード全体
===
: 前の手順からの変更箇所
+++
: 前の手順からの追記箇所
(...)
: 省略
コード全体
<template> <v-card min-width="80%"> (...) === <Chart v-if="loaded" :chartData="chartData" :options="options"/> === <v-progress-circular v-else-if="loaded!=true" indeterminate color="#53e09c" /> </v-card-text> </v-card> </template> <script> +++ import axios from 'axios'; import Chart from './linechart.vue'; export default { (...) data() { return { +++ loaded: false, chartData: { === labels: null, datasets: [{ label: "average value", === data: null, fill: true, backgroundColor: 'rgba(232, 229, 74, 0.2)', borderColor: '#e8e54a', }] }, (...) } }, +++ async created() { +++ const url = "https://www.quandl.com/api/v3/datasets/CHRIS/CME_NK2/data.json?api_key=" + this.$config.quandlKey; +++ const response = await axios.get(url); +++ +++ const data = response.data.dataset_data.data; +++ const last_7day = data.slice(0, 7).reverse(); +++ +++ // // 直近7日の日付 + 終値 +++ const days = last_7day.map(item => item[0]); +++ const last = last_7day.map(item => item[4]); +++ +++ await this.$set(this.chartData, 'labels', days); +++ await this.$set(this.chartData.datasets[0], 'data', last); +++ +++ this.loaded = true; +++ +++ }, } </script>
APIからの情報を取得
まず、画面描画前にデータを取得する処理を実行するために、
created()
関数内でAPIを実行します。
created()
関数は、画面のレンダリング前に実行した処理を記載します。
どのタイミングで、created()が呼ばれるかは、Nuxt Lifecycleにわかりやすいイラストがあります。created() 内では、以下の処理を行っています.
- axios.getで日経平均株価データ取得
- レスポンスデータから、直近7日の情報を取得(slice)、日付の早い順にソート
- map関数で、日付・終値のArrayを抽出
- storeに登録
- 情報取得処理が完了したフラグを立てる
url内で、APIキーを付与するので、
this.$config.quandlKey
でRuntimeConfigから、呼び出しています。async created() { const url = "https://www.quandl.com/api/v3/datasets/CHRIS/CME_NK2/data.json?api_key=" + this.$config.quandlKey; const response = await axios.get(url); const data = response.data.dataset_data.data; const last_7day = data.slice(0, 7).reverse(); // // 直近7日の日付 + 終値 const days = last_7day.map(item => item[0]); const last = last_7day.map(item => item[4]); await this.$set(this.chartData, 'labels', days); await this.$set(this.chartData.datasets[0], 'data', last); this.loaded = true; },データ取得まで、レンダリングを待つ
Chart.jsは、templateのレンダリング次にデータが固定されるので、
- created()で、情報を取得仕切る前に、グラフ部分のレンダリングが行われる
- 結果、空白のグラフが生成される
という問題があります。なので、以下の対策を取ります。
- データの取得が完了するかどうかを判断する変数、
loaded
を定義- templateの<Chart>部分で、
v-if
を用いてレンダリング制御- created()の末尾で、loaded = trueに変更
v-if
で、false状態にして、描画対象から外すことで、初回のレンダリング時に空で描画されるのを回避して、v-if
がtrue状態になった時に、Chartのレンダリングが実行されるようになります。template
レンダリング制御のために、 <Chart>に、
v-if
を追記します。
また、読み込み中にローディング表示したいので、Vuetifyのv-progress-circular
を、v-else-if
で表示するようにします。<template> <v-card min-width="80%"> .... === <Chart v-if="loaded" :chartData="chartData" :options="options"/> === <v-progress-circular v-else-if="loaded!=true" indeterminate color="#53e09c" /> </v-card-text> </v-card> </template>pageに追記すると
API実行中は、ローディング画面になり、
データの読み込みが終わったらグラフ描画が走るようになります。
+++
: 前の手順からの追記箇所
(...)
: 省略<template> <div> <v-row> (...) +++ <v-col cols="12" sm="12" md="6" lg="4"> +++ <outsideAPIChartCard /> +++ </v-col> </v-row> </div> </template> <script> (...) +++ import outsideAPIChartCard from '~/components/charts/chart-card/chart-card-withAPI.vue'; export default { components: { chartCard, storeChartCard, +++ outsideAPIChartCard, } } </script>と実装できました。
まとめ
今回は、Vuetify + Chart.jsで、ダッシュボードに使えそうなグラフのコンポーネントを作成してみました。
今後。GitHubにギャラリー的にまとめようと考えています。
- 投稿日:2021-03-06T19:40:50+09:00
初心者のVuetify | Vuetify + Chart.jsで、グラフカードコンポーネントの作成
ダッシュボードを作成するのに、カード型のUIでグラフ表示をしてみたかったので、
VuetifyとChart.jsで簡単な実装をしてみました。対象読者
以下の内容を書いてます。
- Vuetifyのカードコンポーネント実装
- そこに、Chart.jsの埋め込み
- Nuxt.JSのレンダリングタイミングの制御
できたもの
準備
create-nuxt-app
でプロジェクトを作成します。
参考 インストール - NuxtJS$ yarn create nuxt-app <project-name>
もしくは、GitHubにプロジェクトソースを置いてるので、それを利用してください。
今回は、Vuetifyベースでの記述になります。Nuxt.JSでChart.jsを利用するために、Chart.jsと、vue-chart.jsをインストールします。
yarn add vue-chartjs chart.jsデータをベタガキ
まずは、Chart.jsの設定に直接値を書き込みます。
components/charts/chart-card.vue
グラフ部分作成
まず,Chart.jsのレンダリングをするコンポーネントを記述します。
グラフを描画したい時は、このコンポーネントを呼び出します。
components/charts/chart-card/linechart.vue
<script> import { Line } from 'vue-chartjs'; export default { extends: Line, name: 'chart', props: ['chartData', 'options'], mounted() { this.renderChart(this.chartData, this.options) } } </script>カード作成 + グラフ描画
components/charts/chart-card/chart-card.vue
Vuetifyの、
v-card
でカード部分を実現。
メインコンテンツを実装するv-card-text
に、グラフ部分のコンポーネントを配置します。template部
<template> <v-card min-width="80%"> <v-card-title> Simple Chart </v-card-title> <v-card-text class="chart-container"> <Chart :chartData="chartData" :options="options"/> </v-card-text> </v-card> </template>script部
data()で、グラフ描画に必要な情報を記述
chartData
- labels: X軸のラベル (Array)
- datasets
- label: 各線の凡例
- data: データの数値
- fill: 線グラフより下を塗りつぶすか
- backgroundColor: 塗りつぶす色
- borderColor: 線の色
options
- responsive: レスポンシブのOn/Off
- maintainAspectRatio: 縦横比を自動調整するか
- legend: 凡例の設定
<script> import Chart from './linechart.vue'; export default { components: { Chart }, data() { return { chartData: { labels: ['A', 'B', 'C'], datasets: [{ label: "sample", data: [100, 150, 300], fill: true, backgroundColor: 'rgba(83, 224, 156, 0.2)', borderColor: '#53e09c' }] }, options: { responsive: true, maintainAspectRatio: false, legend: { display: true }, } } }, } </script>これをpageで描画
作成した、カードコンポーネントをpageで描画します。
<template> <div> <v-row> <v-col cols="12" sm="12" md="6" lg="4"> <chartCard /> </v-col> </v-row> </div> </template> <script> import chartCard from '~/components/charts/chart-card/chart-card.vue'; export default { components: { chartCard, } } </script>Vue Storeからの取得
components/charts/chart-card-withStore.vue
ベタガキで使うタイミングなんてそんなにないので、
よくNuxtで用いるパターンとして、Vuexを使うパターンで実装します。ベタ描き部分との差分のみ記述します
Store (Vuex)を作成
store/chart.js
に、Chart用のStoreを作成。参考
* 【Vue.js】Vuexの使い方を解説【データ保存】 | CodeCampusexport const state = () => ({ simple: { label: ['AA', 'AB', 'AC'], data: [1000, 2000, 2500] }, })カード作成 + グラフ描画
this.$store.state...
で、NuxtのどこからでもStoreの値にアクセス可能なので、
chartDataの、labelsとdataの情報を、Storeから引っ張ってくるように記述。
+++
: 前の手順からの追記箇所... <script> import Chart from './linechart.vue'; export default { (...) data() { return { (...) chartData: { +++ labels: this.$store.state.charts_store.simple.label, datasets: [{ label: "sample", +++ data: this.$store.state.charts_store.simple.data, fill: true, backgroundColor: 'rgba(235, 70, 52, 0.2)', borderColor: '#eb4634', }] }, (...) } </script>pageに追記すると
+++
: 前の手順からの追記箇所
(...)
: 省略<template> <div> <v-row> (...) +++ <v-col cols="12" sm="12" md="6" lg="4"> +++ <storeChartCard /> +++ </v-col> </v-row> </div> </template> <script> (...) +++ import storeChartCard from '~/components/charts/chart-card/chart-card-withStore.vue'; export default { components: { chartCard, +++ storeChartCard, } } </script>これで、Storeの情報を使って、グラフ描画できました。
外部APIでの実行
components/charts/chart-card-withAPI.vue
Webアプリで一番使うのが、外部APIから情報を取得して、画面に描画する方法だと思います。なのでそのパターンを実装してみましょう。
今回は、日経平均株価のサンプルでやるために、Quandlで取得してみます。
RuntimeConfig | 外部APIのアクセストークン管理
APIを活用する時に、一番問題なのは、Nuxt.jsにToken情報などが晒されてしまう問題なので、今回は、Nuxt.jsのRuntimeConfigを使います、
かなり重要
本来は、privateRuntimeConfig
を使うのですが、SSR化が必要だったりと色々設定が必要なので、今回は簡単するために、publicRuntimeConfig
で実装します。ブラウザでAPIリクエストするので、アクセストークンは見えてしまう実装となります。完全に隠す必要がある場合は、SSR化して、privateRuntimeConfigで実装する必要があります。publicRuntimeConfigの設定
APIアクセストークンを管理するために以下の手順を取ります。
- 環境変数ファイル '.env'を作成
- .envファイルの中を
nuxt.config.js
から読み込み環境変数ファイル作成
Nuxt.jsにおいて、環境変数情報は、.envファイルで管理します。
QUANDL_KEY=<Quandlのアクセストークン>そして、
nuxt.config.js
に、process.env.変数名
で .envファイルの中見を参照できます。publicRuntimeConfig: { quandlKey: process.env.QUANDL_KEY | <トークンベタガキ> },利用方法
コンポーネント等から、
this.$config.quandlKey
とすることで、環境変数の値を参照することができます。カード作成 + グラフ作成
コード全体
===
: 前の手順からの変更箇所
+++
: 前の手順からの追記箇所
(...)
: 省略
コード全体
<template> <v-card min-width="80%"> (...) === <Chart v-if="loaded" :chartData="chartData" :options="options"/> === <v-progress-circular v-else-if="loaded!=true" indeterminate color="#53e09c" /> </v-card-text> </v-card> </template> <script> +++ import axios from 'axios'; import Chart from './linechart.vue'; export default { (...) data() { return { +++ loaded: false, chartData: { === labels: null, datasets: [{ label: "average value", === data: null, fill: true, backgroundColor: 'rgba(232, 229, 74, 0.2)', borderColor: '#e8e54a', }] }, (...) } }, +++ async created() { +++ const url = "https://www.quandl.com/api/v3/datasets/CHRIS/CME_NK2/data.json?api_key=" + this.$config.quandlKey; +++ const response = await axios.get(url); +++ +++ const data = response.data.dataset_data.data; +++ const last_7day = data.slice(0, 7).reverse(); +++ +++ // // 直近7日の日付 + 終値 +++ const days = last_7day.map(item => item[0]); +++ const last = last_7day.map(item => item[4]); +++ +++ await this.$set(this.chartData, 'labels', days); +++ await this.$set(this.chartData.datasets[0], 'data', last); +++ +++ this.loaded = true; +++ +++ }, } </script>
APIからの情報を取得
まず、画面描画前にデータを取得する処理を実行するために、
created()
関数内でAPIを実行します。
created()
関数は、画面のレンダリング前に実行した処理を記載します。
どのタイミングで、created()が呼ばれるかは、Nuxt Lifecycleにわかりやすいイラストがあります。created() 内では、以下の処理を行っています.
- axios.getで日経平均株価データ取得
- レスポンスデータから、直近7日の情報を取得(slice)、日付の早い順にソート
- map関数で、日付・終値のArrayを抽出
- storeに登録
- 情報取得処理が完了したフラグを立てる
url内で、APIキーを付与するので、
this.$config.quandlKey
でRuntimeConfigから、呼び出しています。async created() { const url = "https://www.quandl.com/api/v3/datasets/CHRIS/CME_NK2/data.json?api_key=" + this.$config.quandlKey; const response = await axios.get(url); const data = response.data.dataset_data.data; const last_7day = data.slice(0, 7).reverse(); // // 直近7日の日付 + 終値 const days = last_7day.map(item => item[0]); const last = last_7day.map(item => item[4]); await this.$set(this.chartData, 'labels', days); await this.$set(this.chartData.datasets[0], 'data', last); this.loaded = true; },データ取得まで、レンダリングを待つ
Chart.jsは、templateのレンダリング次にデータが固定されるので、
- created()で、情報を取得仕切る前に、グラフ部分のレンダリングが行われる
- 結果、空白のグラフが生成される
という問題があります。なので、以下の対策を取ります。
- データの取得が完了するかどうかを判断する変数、
loaded
を定義- templateの<Chart>部分で、
v-if
を用いてレンダリング制御- created()の末尾で、loaded = trueに変更
v-if
で、false状態にして、描画対象から外すことで、初回のレンダリング時に空で描画されるのを回避して、v-if
がtrue状態になった時に、Chartのレンダリングが実行されるようになります。template
レンダリング制御のために、 <Chart>に、
v-if
を追記します。
また、読み込み中にローディング表示したいので、Vuetifyのv-progress-circular
を、v-else-if
で表示するようにします。<template> <v-card min-width="80%"> .... === <Chart v-if="loaded" :chartData="chartData" :options="options"/> === <v-progress-circular v-else-if="loaded!=true" indeterminate color="#53e09c" /> </v-card-text> </v-card> </template>pageに追記すると
API実行中は、ローディング画面になり、
データの読み込みが終わったらグラフ描画が走るようになります。
+++
: 前の手順からの追記箇所
(...)
: 省略<template> <div> <v-row> (...) +++ <v-col cols="12" sm="12" md="6" lg="4"> +++ <outsideAPIChartCard /> +++ </v-col> </v-row> </div> </template> <script> (...) +++ import outsideAPIChartCard from '~/components/charts/chart-card/chart-card-withAPI.vue'; export default { components: { chartCard, storeChartCard, +++ outsideAPIChartCard, } } </script>と実装できました。
まとめ
今回は、Vuetify + Chart.jsで、ダッシュボードに使えそうなグラフのコンポーネントを作成してみました。
今後。GitHubにギャラリー的にまとめようと考えています。
- 投稿日:2021-03-06T19:18:43+09:00
webpackでvueを始めてみる
はじめに
これからvueを学んでいくのですが、CDNを利用するのは卒業したいなと思ったので、今回はwebpackを使ってvueの環境構築をしていきます。
ディレクトリは以下のようになります。
ディレクトリ構造
vue-application/ ├ node_modules/ ├ dist/ | └ budle.js ├ src/ | └ app.js | └ components/ | └ sample-component.vue ├ index.html ├ package.json ├ package-lock.json └ webpack.config.js必要なパッケージのインストール
// package.jsonの作成 $ npm init -y // webpack本体 $ npm install -D 'webpack' //webpackのcli $ npm install -D webpack-cli //vue本体 $ npm install -D vue //vueコンポーネントを変換する $ npm install -D vue-loader //templateをコンパイルする $ npm install -D vue-template-compiler ---------------------------------------- //一括でインストール $ npm install -D webpack webpack-cli vue ...それぞれのパッケージの詳細は以下を参照してください。
webpack
webpack-cli
vue
vue-loader
vue-template-compilerwebpack.config.jsの記述
webpackを使うにはこのファイルに設定を記述する必要があります。
webpack.config.jsconst path = require('path'); const VueLoaderPlugin = require("vue-loader/lib/plugin"); module.exports = { mode: 'development', entry: './src/app.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist'), }, module: { rules: [ { test: /\.vue$/, // ファイルが.vueで終われば... loader: "vue-loader", // vue-loaderを使う }, ] }, resolve: { // Webpackで利用するときの設定 alias: { vue$: "vue/dist/vue.esm.js" }, }, plugins: [ // Vueを読み込めるようにするため new VueLoaderPlugin() ], };mode
mode
はwebpack4から追加された項目です。
パラメータ配下のいずれかを選択できるようになっています。
- development
- production
- none
それぞれの環境に対応した最適化を有効にすることができます。
デフォルト値はproductionです。entry
エントリーポイントを設定できます。
依存関係グラフの構築を開始するために使用すべきモジュールを示します。
デフォルトでは、./src/index.js になっているため変更しました。output
webpackが出力するファイルを設定できます。
module
loaderを使用できるようにする設定です。
vue-loaderの他にもcss-loaderやts-loaderの設定もここに記述します。package.jsonの編集
package.json{ "name": "vue_application", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack --mode development --watch" // 追加 }, "author": "", "license": "ISC", "devDependencies": { "vue": "^2.6.12", "vue-loader": "^15.9.6", "vue-template-compiler": "^2.6.12", "webpack": "^5.24.3", "webpack-cli": "^4.5.0", "webpack-dev-server": "^3.11.2" } }各ファイルの作成
index.html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> <sample-component></sample-component> </div> <script src="./dist/bundle.js"></script> </body> </html>app.jsimport Vue from 'vue'; import SampleComponent from './components/sample-component.vue'; const app = new Vue({ el: "#app", components: { SampleComponent } })sample-component.vue<template> <p>{{ message }}</p> </template> <script> export default { data() { return { message: 'Hello, Vue!' } }, } </script>最後に以下のコマンドを叩いてindex.htmlにアクセス
$ npm run dev
- 投稿日:2021-03-06T18:22:12+09:00
Vuetifyとはなんぞ
今回はVue.jsで使用するフレームワークについて触れていきます!
①Vuetifyとは
簡単に言うと、CSSを使わなくてもおしゃれなボタンとかができる画期的なulのフレームワークみたいです!
▶ul? https://qiita.com/akari_0618/items/ac6eb7fae89f2329ed2cこれを使うことによって、CSSでたくさん書かなくても、言い訳ですね?
使い方は、Vuetifyを使いたいフォルダの中に移動して、ターミナルで以下のように打ち込むと使えるみたいです?
% vue add vuetify % npm run serverで使えるようです!!
ちなみにこれはHTMLの中に埋め込まれています。
たとえばこんな感じですね。index.html<v-alert color="red" dismissible icon="$mdiAccount" type="success" >クリック</v-alert>みたいな。
初めこの文字列をみたときはてな?ってなる駆け出しさんいると思いますが、落ち着いて行きましょう?
表示はこうなります。
CSSを入力しなくてもここまでできるのは助かりますね?いろいろあったので、公式さん貼っときます?
https://vuetifyjs.com/ja/components/alerts/#section-4f7f304465b9
- 投稿日:2021-03-06T17:54:17+09:00
コンポーネント
今回はコンポーネントについて学んでいきます!!
Vue.jsについては後で触っていきます。①コンポーネントとは
ソフトウェアやシステムを構成する要素のことを言います!
またこれらは、単体ではなく、他のプログラムに呼び出されたりしたときに使います!!
なんでこれを使うかというと、機能の追加・修正・削除などが発生した場合に、コンポーネント単位で対応することができるからです!?②モジュールとは
コンポーネントと同じ意味をもつ部品にモジュールというものがあります!
モジュールとコンポーネントの違いは、単体で使えるか使えないかみたいです!?更にここからVueの中でコンポーネントが2つに別れます。
今回はVue.jsをまだ勉強してないのでとりあえずこんなもんなのね。ってことでさらっとだけみていきます。1)グローバルコンポーネント
全てのルート Vue インスタンス(new Vue)のテンプレート内で使用できることを意味します。
2)ローカルコンポーネント
他のサブコンポーネント内では使用できないみたいです!
今回のはコードを読みたくて調べたメモなので参考にならないと思いますが(^_^;)
必要になったら引き続き戻って追記していきます?
- 投稿日:2021-03-06T16:54:13+09:00
Vue.jsのテーブルで動的にモーダルを表示させる
状況
Vue.jsでテーブルを作成し、テーブルのデータごとに動的にモーダルを表示させようとするも、何かおかしい。
前提
$ vue --version @vue/cli 4.5.11なお、本記事では Vue Materialのテーブルコンポーネント を使用しています。
百聞は一見にしかずということで
状況をアニメーションで貼っておきます。
見ての通り、どのモーダルボタンをクリックしても3番目の要素しか表示されない。
しかも背景がかなり暗い。出来れば親でモーダルを発火させるボタンを押せて、モーダルの状態を親で管理できるようにしたい。
該当コード
Parent.vue<template> <div> <md-table v-model="products"> <md-table-row slot="md-table-row" slot-scope="{ item }"> <md-table-cell md-label="ID">{{ item.id }}</md-table-cell> <md-table-cell md-label="ユーザー名">{{ item.user }}</md-table-cell> <md-table-cell md-label="アクション"> <button @click="modalState = true">モーダル表示</button> <md-dialog :md-active.sync="modalState"> <Child :user="item.user" :item-id="item.id" /> </md-dialog> </md-table-cell> </md-table-row> </md-table> </div> </template> <script lang="ts"> import Child from './Child.vue' import Vue from 'vue' export default Vue.extend({ name: 'TableContent', components: { Child, }, data() { return { products: [ { id: 1, user: '伊藤' }, { id: 2, user: '田中' }, { id: 3, user: '佐久間' }, ], modalState: false, } }, }) </script>Child.vue<template> <div style="text-align: center">{{ itemId }} : {{ user }}</div> </template> <script lang="ts"> import Vue from 'vue' export default Vue.extend({ name: 'Vue:Child', props: { itemId: { type: Number, required: true, }, user: { type: String, default: '', }, }, }) </script>問題の原因
初歩的なミスをしていました。
modalState
にテーブルごとのユニークな値を与えていなかった為、ボタンを押すと全てのモーダルがtrueとなり、全て重なって表示されており、背景も暗くなっているということでした。コード修正
Parent.vue<template> <div> <md-table v-model="products"> <md-table-row slot="md-table-row" slot-scope="{ item }"> <md-table-cell md-label="ID">{{ item.id }}</md-table-cell> <md-table-cell md-label="ユーザー名">{{ item.user }}</md-table-cell> <md-table-cell md-label="アクション"> <button @click="$set(modalState, item.id, true)">モーダル表示</button> <!-- テーブルごとのmodalStateを制御 --> <md-dialog :md-active.sync="modalState[item.id]"> <!-- ユニークな値を追加 --> <Child :user="item.user" :item-id="item.id" /> </md-dialog> </md-table-cell> </md-table-row> </md-table> </div> </template> <script lang="ts"> import Child from './Child.vue' import Vue from 'vue' export default Vue.extend({ name: 'TableContent', components: { Child, }, data() { return { products: [ { id: 1, user: '伊藤' }, { id: 2, user: '田中' }, { id: 3, user: '佐久間' }, ], modalState: {}, // 定義をオブジェクト形式に変更 } }, }) </script>無事動的にモーダルを表示することができました!
参考
https://stackoverflow.com/questions/49580396/vuetify-how-to-open-and-close-dialogs-within-a-data-table
https://vuematerial.io/components/table
- 投稿日:2021-03-06T14:17:13+09:00
【随時更新】ポートフォリオ・SaaS開発の参考に!超本格的なRails製オープンソースたち!【初心者もおすすめ】
「プログラマーになってすごいサービスを作りたい!」
という動機でプログラミングを始めてみたものの、いざ勉強を始めてみると
「どんな技術を使って開発したらいいの?」
「どこまで作り込めばいいのだろう?」
「一応完成したけどまだ足りないところがあるのでは?」と不安に感じたことは一度はありますよね?
そんな方に強くオススメしたいRailsアプリケーションのGitHubリポジトリたちを紹介します。
ここで紹介するRailsアプリケーションはオープンソースにもかかわらず、
実際にWebサービスとして運用されていたり、有料で提供されていたりするものもありますので、必ずサービス開発の参考になるはずです!ぜひ、一読してWebサービス開発の参考にしてみてください!
対象読者
・プログラミング初学者さん
・個人サービスの開発に取り掛かる前の方
・サービス開発の参考資料を探している方
・よりよいサービス開発の参考にしたい方GitHubリポジトリたち
ChatWoot
オープンソースのライブチャットソフトです。
ホームページやランディングページに埋め込むお問い合わせチャットや、それに対するメッセージの管理システムを提供しているサービスです。Vue.js × Ruby on Railsで開発されていて、このコードを勉強してマネするだけでかなり本格的なサービスの開発に繋げられると思います!
初見、かなり難しそうに感じますが、Gemfileを見て「devise」など初学者さんでもわかるライブラリなどがたくさん使われていますので、そういった分かる部分から読み進めていってもいいかもしれません。GitHubリポジトリURL
https://github.com/chatwoot/chatwoot公式ページ
https://www.chatwoot.com/
引用元:https://www.chatwoot.com/static/dashboard-screen-b294bdd1d718312290ec49b6c2a13428.png
引用元:https://www.chatwoot.com/static/widget-ghost-99d99a87a95d9c1731b79af6584218ef.png使われている主な技術
- Ruby on Rails
- Vue.js
- Docker
- CircleCI など
GitLab
GitHubのようにコードを管理することができるサービス。
かなりざっくりとGitHubとの違いを挙げると、自社で用意したサーバーなどにGitHubと同じような環境を構築できるので、セキュリティや情報管理の観点からGitHubは使いたくない!という企業さんに使われることが多いサービスです。
※その他にもGitLabの良いところはたくさんありますが、割愛します!こちらもVue.js × Ruby on Railsで開発されたサービスですが、chatwootと違い、Railsのapp/views内のHTMLファイルのコードも充実しているため、まだJSフレームワークでのフロントエンド開発は視野に入れていないプログラマーさんでも読みやすいところは多いのかな、と思います。
ただし、hamlで書かれているため、人によっては読みにくさを感じるかも…GitHubリポジトリ
https://github.com/gitlabhq/gitlabhq/公式ページ
https://about.gitlab.com/
引用元:https://about.gitlab.com/images/solutions/solutions-create.png使われている主な技術
- Ruby on Rails
- Vue.js
- Docker など
solidus
オープンソースのeコマース構築サービス。
他のリポジトリと比べるとディレクトリ構成が少し特殊かな、という印象です。
こちらはフロントエンドも含めてRuby on Railsで開発されているようですので、その点においては参考にしやすいリポジトリなのかな、と思います。こちら(http://demo.solidus.io/) からでもページを見ることも可能です。
GitHubリポジトリ
https://github.com/solidusio/solidus公式ページ
https://spreecommerce.org/使われている技術
- Ruby on Rails
- CircleCI など
まとめ
こちらの内容は随時更新していきます!
他にも実際にサービスとして提供されている良いオープンソースコードがございましたら教えていただきますと幸いです!
- 投稿日:2021-03-06T13:16:44+09:00
[Vue.js]main.jsのimport { createApp } from 'vue'の'vue'って何者??
(当たり前として捉えられているのかは知らないが)意外とこれ解説してる記事がどこにもなかったので、自分なりの見解を記載。
環境:
PC:mac book pro
バージョン:Vue.js 3
エディタ:VSCode前提:
・Vue.js環境周りを全て設定完了
・(設定直後で)ファイルは何もいじっていない
・localhost:8080でサーバ起動できる上記前提を元に以下ファイルを見ていったときに疑問が1つ。
main.js
import { createApp } from 'vue' ⇦こいつ import App from './App.vue' createApp(App).mount('#app')2行目はまぁ、App.vueをmain.jsにインポートしてるってだけの話だからいいとして、
1行目のこいつ。
createAppはただの関数名(予約語という訳でもなく)と思われるのでここは問題ない。
createAppは関数であるものの、'vue'って何??
そんなファイルどこにもないぞ??何者だこいつ???
(18:47 更新 解釈が全然違っていたため。すでに閲覧された方、ご迷惑をおかけしました。)
以下のコマンドを発行時に、my-projectディレクトリが生成され、その下に色々とファイルが生成される。
vue create my-project
※my-projectの部分は可変
その際に、「package.json」というjsonファイルが作成されるのだが、
どうやらその中に「dependencies」という情報が設定されているようで、(中身は環境によって異なる(バージョンやデータなど))その中のvueを使用するためのインポート文として、「import { createApp } from 'vue'」が記載されているらしい。"dependencies": { "core-js": "^3.6.5", "vue": "^3.0.0" }以上。
- 投稿日:2021-03-06T08:02:00+09:00
単一ファイルコンポーネントの各オプションについて
はじめに
単一ファイルコンポーネントの scriptタグ 内で用いられるオプションについてまとめました。
data
data(){ return{ val:1, dog:{ name:"ぽち” feature:"プードル" } } },ここでdata関数内で定義したプロパティだけがリアクティブになる。(Vuexのstateもだが今回は割愛)
リアクティブとは?
vue.jsが変更を検知する状態のこと。
※dataに定義されたobjectに新しくプロパティを追加した場合、その新しいプロパティはリアクティブにならない。上記の例だとdogオブジェクトにweight:"20kg"を追加してもweightはリアクティブにならない。$setを使えば追加できる。
参考:https://jp.vuejs.org/v2/guide/reactivity.html#ad変更を検知するとその度にbeforeupdate=>updatedの処理が行われる。
ここでライフサイクルフック
mounted前の↓のライフサイクルは1回のみ実行される。
beforeCreate
created
beforeMount
mountedデータの更新(リアクティブデータの変更の検知時)時はrerenderされるため、この前後で以下のフックが実行される。
beforeUpdate
データの更新があった時に、rerenderされる前に実行される。
更新前の既存のDOMに対してアクセスすることができる。
また、beforeUpdateはSSRの場合は使えないので注意が必要。updated
データの更新があった時に、rerenderされた後に実行される。
無限ループ防止のため、状態変化を行うような処理は書いてはいけない。子コンポーネントを全て再描画するためにはmounted同様$nextTickを使う。
また、updatedはSSRの場合は使えないので注意が必要。参考:https://jp.vuejs.org/v2/guide/computed.html#ウォッチャ
computed
基本的には値を返すもの。基本的には変数を取らない。returnが必須。処理の中にdataを含めるとリアクティブにもなる。非同期処理ができない。
computed:{ test2(){ return Math.random()*this.val; }, }一方で例外的に変数をとる場合がある。
その場合、以下のようにgetter関数とsetter関数に分けてかける。
getter関数は引数を取らない。値を返すもの。本来のcomputedの役割の処理をかく。
setter関数は引数を取る。値を返すのではなく値を更新する。後述するmethodsの役割に近い。例では仮引数のnewValueの値を更新している。computed: { fullName: { // getter 関数 get: function () { return this.firstName + ' ' + this.lastName }, // setter 関数 set: function (newValue) { var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } }参考:https://jp.vuejs.org/v2/guide/computed.html#算出-Setter-関数
setter関数の処理はmethodsで書けばいいじゃないか!と思っている方へ。
なぜmethodsの役割に近いsetter関数が必要なのかはcomputedとmethodsの違いのところでかく。methods
基本的には値を更新するもの。変数を取れる。returnはどちらでもいい。
methods: { countUp(size) { this.val = this.val + size; }, }computedとmethodsの違い
1.役割の違い
前述したようにcomputedは値を返すことが基本的な役割(変数を取らず、returnする)
一方でmethodsは値を更新することが基本的な役割(変数を取れる。returnもできる)
2.キャッシュされるかされないか
なぜsetter関数が必要なのかというところの答えになる。
computedでは処理の結果がキャッシュされるが、methodsではキャッシュされない。
どういうこと...?<template> <div> <h1>methods</h1> <span>{{test()}}</span> <span>{{test()}}</span> <span>{{test()}}</span> <h1>computed</h1> <span>{{test2}}</span> <span>{{test2}}</span> <span>{{test2}}</span> </div> </template> <script> export default { data(){ return{ val:1, } }, methods: { test(){ return Math.random(); }, }, computed:{ test2(){ return Math.random(); }, } } </script>上記コードの結果が以下である。
methods 0.85313097662468860.097562584449483620.43239994914384416 computed 0.51703190122513630.51703190122513630.5170319012251363computedは結果がキャッシュされるため3回とも同じ結果が表示される。(2回目3回目は処理を実行せず1回目の結果を流用している。
一方でmethodsは結果がキャッシュされないため3回とも処理が実行されている。そのため結果がことなる。setter関数をなぜ使うのかの話に戻るが、変数を取りたい&キャッシュの仕組みを使ってエコな処理にしたいという時に使える機能。
またこのキャッシュ機能がvue.jsの強みである。通常のjsにはfunctionしかないので、毎回methodsを実行してるイメージ。
watch
watchは一部のプロパティの変更を検知できる。また第一引数に変更後の値、第二引数に変更前の値を取ることができる。
例えば、以下のような場合に使える。data(){ return{ question: '', } } watch: { // この関数は question が変わるごとに実行されます。 question: function (newQuestion, oldQuestion) { this.answer = 'Waiting for you to stop typing...' this.debouncedGetAnswer() } }, //questionは名前統一する
- 投稿日:2021-03-06T08:02:00+09:00
【Vue】単一ファイルコンポーネントの各オプションについて
はじめに
単一ファイルコンポーネントの scriptタグ 内で用いられるオプションについてまとめました。
data
data(){ return{ val:1, dog:{ name:"ぽち" feature:"プードル" } } },ここでdata関数内で定義したプロパティだけがリアクティブになる。(Vuexのstateもだが今回は割愛)
リアクティブとは?
vue.jsが変更を検知する状態のこと。
※dataに定義されたobjectに新しくプロパティを追加した場合、その新しいプロパティはリアクティブにならない。上記の例だとdogオブジェクトにweight:"20kg"を追加してもweightはリアクティブにならない。$setを使えば追加できる。
参考:https://jp.vuejs.org/v2/guide/reactivity.html#ad変更を検知するとその度にbeforeupdate=>updatedの処理が行われる。
ここでライフサイクルフック
mounted前の↓のライフサイクルは1回のみ実行される。
beforeCreate
created
beforeMount
mountedデータの更新(リアクティブデータの変更の検知時)時はrerenderされるため、この前後で以下のフックが実行される。
beforeUpdate
データの更新があった時に、rerenderされる前に実行される。
更新前の既存のDOMに対してアクセスすることができる。
また、beforeUpdateはSSRの場合は使えないので注意が必要。updated
データの更新があった時に、rerenderされた後に実行される。
無限ループ防止のため、状態変化を行うような処理は書いてはいけない。子コンポーネントを全て再描画するためにはmounted同様$nextTickを使う。
また、updatedはSSRの場合は使えないので注意が必要。参考:https://jp.vuejs.org/v2/guide/computed.html#ウォッチャ
computed
基本的には値を返すもの。基本的には変数を取らない。returnが必須。処理の中にdataを含めるとリアクティブにもなる。非同期処理ができない。
computed:{ test2(){ return Math.random()*this.val; }, }一方で例外的に変数をとる場合がある。
その場合、以下のようにgetter関数とsetter関数に分けてかける。
getter関数は引数を取らない。値を返すもの。本来のcomputedの役割の処理をかく。
setter関数は引数を取る。値を返すのではなく値を更新する。後述するmethodsの役割に近い。例では仮引数のnewValueの値を更新している。computed: { fullName: { // getter 関数 get: function () { return this.firstName + ' ' + this.lastName }, // setter 関数 set: function (newValue) { var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } }参考:https://jp.vuejs.org/v2/guide/computed.html#算出-Setter-関数
setter関数の処理はmethodsで書けばいいじゃないか!と思っている方へ。
なぜmethodsの役割に近いsetter関数が必要なのかはcomputedとmethodsの違いのところでかく。methods
基本的には値を更新するもの。変数を取れる。returnはどちらでもいい。
methods: { countUp(size) { this.val = this.val + size; }, }computedとmethodsの違い
1.役割の違い
前述したようにcomputedは値を返すことが基本的な役割(変数を取らず、returnする)
一方でmethodsは値を更新することが基本的な役割(変数を取れる。returnもできる)
2.キャッシュされるかされないか
なぜsetter関数が必要なのかというところの答えになる。
computedでは処理の結果がキャッシュされるが、methodsではキャッシュされない。
どういうこと...?<template> <div> <h1>methods</h1> <span>{{test()}}</span> <span>{{test()}}</span> <span>{{test()}}</span> <h1>computed</h1> <span>{{test2}}</span> <span>{{test2}}</span> <span>{{test2}}</span> </div> </template> <script> export default { data(){ return{ val:1, } }, methods: { test(){ return Math.random(); }, }, computed:{ test2(){ return Math.random(); }, } } </script>上記コードの結果が以下である。
methods 0.85313097662468860.097562584449483620.43239994914384416 computed 0.51703190122513630.51703190122513630.5170319012251363computedは結果がキャッシュされるため3回とも同じ結果が表示される。(2回目3回目は処理を実行せず1回目の結果を流用している。
一方でmethodsは結果がキャッシュされないため3回とも処理が実行されている。そのため結果がことなる。setter関数をなぜ使うのかの話に戻るが、変数を取りたい&キャッシュの仕組みを使ってエコな処理にしたいという時に使える機能。
またこのキャッシュ機能がvue.jsの強みである。通常のjsにはfunctionしかないので、毎回methodsを実行してるイメージ。
watch
watchは一部のプロパティの変更を検知できる。また第一引数に変更後の値、第二引数に変更前の値を取ることができる。
例えば、以下のような場合に使える。data(){ return{ question: '', } } watch: { // この関数は question が変わるごとに実行されます。 question: function (newQuestion, oldQuestion) { this.answer = 'Waiting for you to stop typing...' this.debouncedGetAnswer() } }, //questionは名前統一する