すくまりのメモ帳すくまりのメモ帳
← 記事一覧に戻る

爆速で開発するための Vuetify チートシート

2020-09-05
爆速で開発するための Vuetify チートシート

はじめに

この記事では@vue/cliとVuetifyを使って初めてWeb開発する人向けに、よく使う構文やコンポーネントをまとめました。基本的にコピペで使えるように、単一ファイルコンポーネントを使用しています。

動作環境
  • Vue.js: 2.6
  • @vue/cli: v.4.4.6
  • node: v8.10.0
  • npm: v5.6.0

導入

Vue.cilのインストールとプロジェクトの作成

以下の記事を参考にして@Vue.cil をインストールし、プロジェクトを作成してください (ここでは "my-app"という名前で作成しました)。

https://qiita.com/C\_HERO/items/318fe65871f8e53e8c80

なお、プロジェクトを作成する際は「Manually select features」を選択し、「Router」にチェックを付けておいてください。それ以外の質問は全てYesかEnterでいいです。

Vuetifyのインストール

BashやVsCodeのターミナルなどで、先ほど作成したプロジェクト(ここでは"my-app")に移動し、Vuetifyを導入してください。途中の質問は全てYes(or Enter)でいいです。

# my-appフォルダへ移動
cd my-app
 
# Vuetifyの導入
vue add vuetify
 

Vue Serverの起動

以下のコマンドを実行してhttp://localhost:8080/へアクセスしてください。

npm run serve
 
サーバーが起動しない場合

クローンしてきたプロジェクトだと以下のようなエラーが出る場合があります。

npm ERR! code ELIFECYCLE
npm ERR! errno 126
npm ERR! mywebapp@0.1.0 serve: vue-cli-service serve
npm ERR! Exit status 126
npm ERR!
npm ERR! Failed at the mywebapp@0.1.0 serve script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
 
npm ERR! A complete log of this run can be found in:
npm ERR! /var/root/.npm/_logs/2018-09-25T07_17_36_807Z-debug.log

(引用元:https://github.com/vuejs/vue-cli/issues/2596)

これはモジュールがインストールされていないことによるエラーであることがほとんどなので、以下のコマンドを実行してモジュールをインストールしてください。

npm install

以下の画面が表示されたらVueの導入は完了です。

Appファイルの編集

 src/App.vueファイルのコードを全て削除し、以下のコードをコピペしてください。

Vuetifyのコンポーネントは<v-app></v-app>タグ内で呼ぶ必要があります。

<template>
  <!-- App.vue -->
 
  <v-app>
    <v-navigation-drawer app>
      <!-- -->
    </v-navigation-drawer>
 
    <v-app-bar app>
      <!-- -->
    </v-app-bar>
 
    <!-- Sizes your content based upon application components -->
    <v-main>
      <!-- Provides the application the proper gutter -->
      <v-container fluid>
        <!-- If using vue-router -->
        <router-view></router-view>
      </v-container>
    </v-main>
 
    <v-footer app>
      <!-- -->
    </v-footer>
  </v-app>
</template>
 
<script>
export default {
  name: "App",
  components: {},
  data: () => ({}),
  methods: {},
};
</script>
 
<style scoped></style>
 

(引用:https://vuetifyjs.com/en/components/application/)

また、 src/views/Home.vueの中身を削除し、以下をコピペしてください。

<template>
  <div class="home">
    <h1>This is Home view</h1>
  </div>
</template>
 

以下の画面になれば準備完了です。

Routerについてはまた後で説明しますが、この画面はApp.vueの<router-view></router-view>タグによって、/home のURLパスに割り当てられたHome.vueの画面が表示されています。

以降、Home.vueを編集していきます。

Vuetify チートシート 

 パスの指定方法

vueファイル内でコンポーネント等をimportする際、パスを指定する必要があります。その際、'../../view/Example'のように相対パスで指定してもいいのですが、'@'を使うとsrcフォルダを絶対パスで指定できます。以下例です。

"@/" ← src/
"@/assets/logo.png"
"@/views/Home"

Vuetifyのカラー設定

@/plugins/vuetify.js のexport defaultメソッド内に以下の内容を追加してください。

import Vue from "vue";
import Vuetify from "vuetify/lib";
import colors from "vuetify/lib/util/colors";
 
Vue.use(Vuetify);
 
export default new Vuetify({
  theme: {
    themes: {
      light: {
        primary: colors.red.darken1, // #E53935
        secondary: colors.red.lighten4, // #FFCDD2
        accent: colors.indigo.base, // #3F51B5
        myColor: colors.green.accent4,
        myColor2: colors.blue.lighten1,
      },
    },
  },
});

(引用:https://vuetifyjs.com/ja/styles/colors/)

@/views/VuetifyColor.vueに以下をコピペしてください。

<template>
  <div class="vuetifyColor">
    <h1>This is VuetifyColor view</h1>
    <v-container class="d-flex flex-column">
      <v-btn class="accent">accent</v-btn>
      <v-btn class="myColor">myColor</v-btn>
      <v-btn
        :style="{
          'background-color': this.$vuetify.theme.themes.light.myColor2,
        }"
        >変数として使う場合</v-btn
      >
      <v-btn :color="myColor">算出プロパティを使う場合</v-btn>
    </v-container>
  </div>
</template>
<script>
export default {
  computed: {
    myColor: function () {
      return this.$vuetify.theme.themes.light.myColor2;
    },
  },
};
</script>

classのバインディング方法

@/views/ClassBind.vueを作成し、以下をコピペしてください。

<template>
  <div class="classBind">
    <h1>This is AddClass view</h1>
    <v-container class="d-flex flex-column">
      <v-btn>クラス無し</v-btn>
      <v-btn class="info">基本形</v-btn>
      <v-btn :class="onBlue">クラスオブジェクト使用</v-btn>
      <v-btn :class="{ info: !classOn }">クラスOFF</v-btn>
      <v-btn :class="{ info: classOn }">クラスON</v-btn>
      <v-btn :class="classMethod">メソッド</v-btn>
      <v-btn :class="['warning--text', { success: classOn }]">複合形</v-btn>
    </v-container>
  </div>
</template>
 
<script>
export default {
  name: "ClassBind",
  data: () => ({
    classOn: true,
    onBlue: {
      info: true,
    },
  }),
  computed: {
    classMethod: function() {
      return {
        info: true,
      };
    },
  },
};
</script>
 

また、@/views/Home.vueを以下に書き換えて、AddClass.vueをimportしてください。

<template>
  <div class="home">
    <h1>This is Home view</h1>
    <ClassBind></ClassBind>
  </div>
</template>
 
<script>
import ClassBind from "@/views/ClassBind";
 
export default {
  components: {
    ClassBind,
  },
};
</script>
 

以下の画面が表示されます。

以下、説明に使用するコンポーネントのコードのみを掲載しますが、同様にHome.vue等にコンポーネントを importして使ってください。

styleのバインディング方法

@/views/StyleBind.vueを作成し、以下をコピペしてください。

<template>
  <div class="styleBind">
    <h1>This is StyleBind view</h1>
    <v-container class="d-flex flex-column">
      <v-btn>スタイル無し</v-btn>
      <v-btn style="color: red">基本形</v-btn>
      <v-btn :style="onRed">スタイルオブジェクト使用</v-btn>
      <v-btn :style="styleMethod">メソッド</v-btn>
      <v-btn :style="{ color: 'red', fontSize: '2rem' }">複数形</v-btn>
      <v-btn :style="[onRed, largeFont]">配列形</v-btn>
      <v-btn :style="[onRed, { fontSize: '2rem' }]">複合形</v-btn>
    </v-container>
  </div>
</template>
 
<script>
export default {
  name: "StyleBind",
  data: () => ({
    styleOn: true,
    onRed: {
      color: "red",
    },
    largeFont: {
      fontSize: "2rem",
    },
  }),
  computed: {
    styleMethod: function() {
      return {
        color: "red",
      };
    },
  },
};
</script>
 

子コンポーネントのCheckboxの使い方

@/views/ParentCheckbox.vueを作成し、以下をコピペしてください。

<template>
  <div class="parentCheckbox">
    <ChildCheckbox
      :parentSelected="selected"
      :parentData="data"
      @update-data="update"
    ></ChildCheckbox>
    {{ selected }}
  </div>
</template>
 
<script>
import ChildCheckbox from "@/components/ChildCheckbox";
export default {
  name: "ParentCheckbox",
  data: () => ({
    selected: [],
    data: [
      { id: 0, name: "dog" },
      { id: 1, name: "cat" },
      { id: 2, name: "cow" },
    ],
  }),
  components: {
    ChildCheckbox,
  },
  methods: {
    update: function (newVal) {
      this.selected = newVal;
    },
  },
};
</script>
 

@/components/ChildCheckbox.vueを作成し、以下をコピペしてください。

<template>
  <div class="childCheckbox">
    <v-container>
      <template v-for="(item, index) in parentData">
        <v-checkbox
          v-model="selected"
          :label="item.name"
          :value="item.id"
          :key="`childCheckbox-${index}`"
        ></v-checkbox>
      </template>
    </v-container>
  </div>
</template>
 
<script>
export default {
  name: "ChildCheckbox",
  props: {
    parentSelected: {
      type: Array,
      default: function () {
        return [];
      },
    },
    parentData: {
      type: Array,
      default: function () {
        return [];
      },
    },
  },
  computed: {
    selected: {
      get() {
        return this.parentSelected;
      },
      set(newVal) {
        this.$emit("update-data", newVal);
      },
    },
  },
};
</script>
 

vueのドキュメントを見ると、v-modelを使った実装をよく見かけますが、子コンポーネントの場合はその実装を行うことができません。なぜならpropsで送ったデータに対して直接変更を行うとエラーになるからです。

子コンポーネントに変数を送った場合、基本的にはcomputedを一枚かませて利用してください。

VeeValidateを使ったバリデーション

以下のコマンドを`my-app`フォルダ内で実行してください。

npm install vee-validate --save

@/main.jsに以下をコピペして追加してください。

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import vuetify from "./plugins/vuetify";
import {
  extend,
  ValidationObserver,
  ValidationProvider,
  setInteractionMode,
} from "vee-validate";
import * as rules from "vee-validate/dist/rules";
import { messages } from "vee-validate/dist/locale/en.json";
 
Vue.config.productionTip = false;
 
Object.keys(rules).forEach((rule) => {
  extend(rule, {
    ...rules[rule],
    message: messages[rule],
  });
});
 
Vue.component("ValidationProvider", ValidationProvider);
Vue.component("ValidationObserver", ValidationObserver);
setInteractionMode("aggressive");
 
new Vue({
  router,
  vuetify,
  render: (h) => h(App),
}).$mount("#app");
 

@/views/Validation.vueを作成し、以下をコピペしてください。

<template>
  <div class="validation">
    <ValidationObserver ref="observer" v-slot="{ invalid }">
      <v-form>
        <ValidationProvider
          name="Name"
          rules="required|alpha|max:10"
          v-slot="{ errors }"
        >
          <v-text-field
            v-model="name"
            :counter="10"
            :error-messages="errors"
            label="Name"
            required
          ></v-text-field>
        </ValidationProvider>
        <ValidationProvider
          name="Number"
          rules="required|numeric|max:4"
          v-slot="{ errors }"
        >
          <v-text-field
            v-model="num"
            :counter="4"
            :error-messages="errors"
            label="Number"
            required
          ></v-text-field>
        </ValidationProvider>
        <ValidationProvider name="Radio" rules="required" v-slot="{ errors }">
          <v-radio-group
            v-model="radioGroup"
            :mandatory="false"
            :error-messages="errors"
          >
            <v-radio label="No" :value="false"></v-radio>
            <v-radio label="Yes" :value="true"></v-radio>
          </v-radio-group>
        </ValidationProvider>
        <v-btn class="mr-4" @click="submit" :disabled="invalid">
          submit
        </v-btn>
        <v-btn class="mr-4" @click="check">
          check
        </v-btn>
        <v-btn @click="clear">
          clear
        </v-btn>
      </v-form>
    </ValidationObserver>
  </div>
</template>
 
<script>
export default {
  name: "Validation",
  data: () => ({
    name: "",
    num: "",
    radioGroup: null,
    errors: null,
  }),
 
  methods: {
    submit() {
      this.$refs.observer.validate();
      alert(
        `name: ${this.name}\nnum: ${this.num}\nradioGroup: ${this.radioGroup}`
      );
    },
    check() {
      const error = [];
      if (!this.isAlphaOnly(this.name)) error.push(this.name);
      if (!this.isNumber(this.num)) error.push(this.num);
      if (!this.isBool(this.radioGroup)) error.push(this.radioGroup);
 
      const errorMessage = error.map((value) => {
        return `${value} is invalid.\n`;
      });
      if (error.length > 0) alert(errorMessage);
    },
    clear() {
      this.name = "";
      this.num = "";
      this.radioGroup = null;
      this.$refs.observer.reset();
    },
    isAlphaOnly(value) {
      try {
        return !value.match(/[^a-z]/gi);
      } catch (error) {
        return false;
      }
    },
    isNumber(value) {
      try {
        return typeof value === "number";
      } catch (error) {
        return false;
      }
    },
    isBool(value) {
      try {
        return typeof value === "boolean";
      } catch (error) {
        return false;
      }
    },
  },
};
</script>
 

Javascriptの頻出繰り返し構文

@/views/JavascriptMethod.vueを作成し、以下をコピペしてください。

<template>
  <div id="JavascriptMethod">
    <h3>CowFilter</h3>
    {{ filterMethod }}
 
    <h3>map</h3>
    {{ mapMethod }}
 
    <h3>forEach</h3>
    {{ forEachMethod }}
 
    <h3>find</h3>
    {{ findMethod }}
 
    <h3>some</h3>
    {{ someMethod }}
 
    <h3>for in</h3>
    {{ forInMethod }}
 
    <h3>for of</h3>
    {{ forOfMethod }}
 
    <h3>Object.keys</h3>
    {{ objectKeysMethod }}
 
    <h3>Object.values</h3>
    {{ objectValuesMethod }}
  </div>
</template>
<script>
export default {
  name: "JavascriptMethod",
  data: () => ({
    data: [
      { id: 0, name: "dog" },
      { id: 1, name: "cat" },
      { id: 2, name: "cow" },
      { id: 3, name: "cow" },
    ],
    objectData: {
      a: "somestring",
      b: 42,
      c: false,
    },
  }),
  computed: {
    filterMethod: function () {
      return this.data.filter((item) => {
        return item.name === "cow";
      });
    },
    mapMethod: function () {
      return this.data.map((item, index) => {
        return { id: item.id * 2, name: item.name, index: index };
      });
    },
    forEachMethod: function () {
      const result = [];
      this.data.map((item, index) => {
        result.push({ id: item.id, name: item.name, index: index });
      });
      return result;
    },
    findMethod: function () {
      return this.data.find((item) => {
        return item.name === "cow";
      });
    },
    someMethod: function () {
      const result = [];
      this.data.some((item, index) => {
        if (item.name === "cow") {
          return true;
        }
        result.push(index);
      });
      return result;
    },
    forInMethod: function () {
      const result = [];
      for (const key in this.data) {
        result.push(this.data[key]);
      }
      return result;
    },
    forOfMethod: function () {
      const result = [];
      for (const item of this.data) {
        result.push(item.name);
      }
      return result;
    },
    objectKeysMethod: function () {
      return Object.keys(this.objectData);
    },
    objectValuesMethod: function () {
      return Object.values(this.objectData);
    },
  },
};
</script>

トップページへ戻る