Vue.jsで〇〇なときどう書く？

# はじめに

Vue.jsを書いている時に、「〇〇なときどう書いたっけな？」と困ることがあったりするので、備忘録として残していくだけの記事です。
では始めていきましょう。

## 前提

ここで紹介するVueファイルを構成するソースコードは、 `Pug / TypeScript / Scss` とします。
また、UIコンポーネントにElementUIを採用しています。

## ソースコード

githubにPushしてます。
https://github.com/ryo-is/vue-demo-sample

## 1. onClickやonChangeで関数を呼びたいとき

`@click`　や `@change` を使う。
この `@hoge` というのは `v-on:hoge` 表記の省略形です。

### 1-1. ClickEvent

下記の例では、ボタンをクリックすると `countNumber` を１ずつ増やしています。

```pug:template
.click-event
  .child-title ClickEvent デモ
  el-row
    el-col(:span="16")
      el-button(@click="addCount") カウントアップ
    el-col(:span="2")
      .count-number {{ countNumber }}
```

```ts:script
import { Component, Vue } from "vue-property-decorator";

@Component({})
export default class ClickEventComponent extends Vue {
  public countNumber: number = 0;

  public addCount() {
    this.countNumber += 1;
  }
}
```

### 1-2. ChangeEvent

セレクトボックスの選択肢を変更した時にテキストの内容 ( `selectedText` ) を書き換えています。

```pug:template
.change-event
  .child-title ChangeEvent デモ
  el-row
    el-col(:span="16")
      el-select(v-model="selectItem" @change="changeItem")
        el-option(label="Apple" value="Apple") Apple
        el-option(label="Banana" value="Banana") Banana
        el-option(label="Orange" value="Orange") Orange
    el-col(:span="4")
      .select-text {{ selectedText }}
```

```ts:script
import { Component, Vue } from "vue-property-decorator";

@Component({})
export default class ChangeEventComponent extends Vue {
  public selectItem: string = "";
  public selectedText: string = "";

  public changeItem() {
    this.selectedText = this.selectItem;
  }
}
```

## 2. 要素の表示 / 非表示を切り替えたいとき

`v-show` や `v-if` を使う。

### 2-1. v-show の場合

ボタンをクリックした時に `displayFlag` を切り替えています。
trueなら `display:block` 、falseなら `display:none` となります。

```pug:template
.v-show
  .child-title v-showによる表示/非表示切り替え
  .button-area
    el-button(@click="displayFlag=!displayFlag") 切り替えボタン
  img.logo-img(v-show="displayFlag" alt="" src="../../assets/logo.png")
```

```ts:script
import { Component, Vue } from "vue-property-decorator";

@Component({})
export default class vShowComponent extends Vue {
  public displayFlag: boolean = true;
}
```

### 2-2. v-if の場合

`v-show` とは少し違って条件式を書くことができるので、表示/非表示の状態を、割と自由に設定することができます。
下記の例では、ボタンをクリックした時に `displayFlag` を `indicate` または `hide` に切り替えています。
`indicate` なら `display:block` 、 `hide` なら `display:none` となります。

```pug:template
.v-if
  .child-title v-ifによる表示/非表示切り替え
  .button-area
    el-button(@click="changeDisplayFlag") 切り替えボタン
  img.logo-img(v-if="displayFlag==='indicate'" alt="" src="../../assets/logo.png")
```

```ts:script
import { Component, Vue } from "vue-property-decorator";

@Component({})
export default class vIfComponent extends Vue {
  public displayFlag: string = "indicate";

  public changeDisplayFlag() {
    if (this.displayFlag === "indicate") {
      this.displayFlag = "hide";
    } else {
      this.displayFlag = "indicate";
    }
  }
}
```

## 3. 要素の表示 / 非表示にアニメーションを付けたいとき

`transition` を使う。
transitionとは、Vueがデフォルトで用意してくれている、 DOM からアイテムが追加、更新、削除されたときにトランジション効果を適用するための方法です。
先程の `v-show` のソースコードを少し書き換えます。

```pug:template
.v-show
  .child-title v-show デモ
  .button-area
    el-button(@click="displayFlag=!displayFlag") 切り替えボタン
  transition(name="image-transition")
    img.logo-img(v-show="displayFlag" alt="" src="../../assets/logo.png")
```

transitionのnameで渡している値 (上記のソースコード内で言う `image-transition` ) が、Styleの `image-transition-enter-active` に対応するので、Styleを調整するときは下記のように書くことができます。

```scss:style
.v-show {
  .image-transition-enter-active,
  .image-transition-leave-active {
    transition: all .5s;
  }

  .image-transition-enter,
  .image-transition-leave-to {
    opacity: 0;
    transform: translateY(-10px);
  }
}
```

## 4. 要素を繰り替えし生成したいとき

`v-for` を使う。
下記の例では `["Apple", "Banana", "Orange"]` という文字列をli要素として生成しています。

```pug:template
.v-for
  .child-title v-for デモ
  ul.list-items
    li(v-for="item in listItems" :key="item") {{ item }}
```

```ts:script
import { Component, Vue } from "vue-property-decorator";

@Component({})
export default class vForComponent extends Vue {
  public listItems: string[] = ["Apple", "Banana", "Orange"];
}
```

## 5. inputの要素を変数に保存したいとき

`v-model` を使う。
下記の例では、テキストエリアに入力した値を、別要素のテキストとして表示しています。

```pug:template
.v-model
  .child-title v-model デモ
  el-row
    el-col(:span="10")
      el-input(v-model="inputText")
    el-col(:span="14")
      .input-text {{ inputText }}
```

```ts:script
import { Component, Vue } from "vue-property-decorator";

@Component({})
export default class vModelComponent extends Vue {
  public inputText: string = "";
}
```

## 6. 親Componentの関数を子Componentから呼び出したいとき

`$emit` を使う。
下記の例では、子Componentのボタンをクリックしたら、親Componentのテキストを非表示にしたり、表示したりしています。

### 子Component

```pug:template(ChildComponent)
.emit
  .child-title $emit デモ
  el-button(@click="$emit('changeTitleDisplay')") タイトル表示/非表示
```

### 親Component

```pug:template(ParentComonent)
.home
  .title(v-show="titleDisplay") {{ homeTitle }}
  emitComponent(v-on:changeTitleDisplay="changeTitleDisplay")
```

```ts:script(ParentComonent)
import { Component, Vue } from "vue-property-decorator";
import emitComponent from "@/components/emit/Emit.vue";

@Component({
  components: {
    emitComponent,
  },
})
export default class Home extends Vue {
  public homeTitle: string = "Vue Demo Sapmle";
  public titleDisplay: boolean = true;

  public changeTitleDisplay() {
    this.titleDisplay = !this.titleDisplay;
  }
}
```

## 7. 動的にclassを切り替えたいとき

`v-bind:class` を使う。
下記の例では、タイトルのClassを変更することで、文字色を変更しています。

```pug:template
.change-class
  .child-title(v-bind:class="fontColorClass") Change Class デモ
  el-button(@click="changeFontColor('red')") 赤色に変更
  el-button(@click="changeFontColor('yellow')") 黄色に変更
  el-button(@click="changeFontColor('normal')") 元に戻す
```

```ts:script
import { Component, Vue } from "vue-property-decorator";

@Component({})
export default class ChangeClassComponent extends Vue {
  public fontColorClass: string = "normal-font-color";

  public changeFontColor(color: string) {
    this.fontColorClass = color + "-font-color";
  }
}
```

```scss:style
.change-class {
  .red-font-color {
    color: red;
  };

  .yellow-font-color {
    color: yellow;
  }
}
```

## 8. 子Componentに値を渡したいとき

`Prop` を使う。
下記の例では、親Componentから子Componentに直接テキストを渡したり、インスタンス変数を渡したりしています。

### 子Component

```pug:template(ChildComponent)
.prop
  .child-title Prop デモ
  .prop-text {{ propText }}
  .prop-value {{ propValue }}
```

```ts:script(ChildComponent)
import { Component, Vue, Prop } from "vue-property-decorator";

@Component({})
export default class PropComponent extends Vue {
  @Prop() public propText: string;
  @Prop() public propValue: string;
}
```

### 親Component

```pug:template(ParentComponent)
.home
  propComponent(:prop-text="'Propで値を子Componentに渡す'", :prop-value="propValue")
```

```ts:script(ParentComponent)
import { Component, Vue } from "vue-property-decorator";
import propComponent from "@/components/prop/Prop.vue";

@Component({
  components: {
    propComponent,
  },
})
export default class Home extends Vue {
  public propValue: string = "インスタンス変数を子Componentに渡す";
}

```

## 9. 親Componentから子Componentを識別したいとき (2019/01/10 追記)

`ref` を使う。
親コンポーネントの `template` に子コンポーネントを読み込むときに `ref` 属性を定義すると `vm.$refs` で子コンポーネントを識別することができます。
ただ注意が必要なのが `created()` の段階ではまだ子コンポーネントがインスタンス化されていないので取得できません。 `mounted()` やClickなどのイベントなどで取得しましょう。
下記の例では子コンポーネントの変数を取得して、標準出力しています。

### 子Component

```pug:template(ChildComponent)
.ref
  .child-title Ref デモ
```

```ts:script(ChildComponent)
import { Component, Vue } from "vue-property-decorator";

@Component({})
export default class RefComponent extends Vue {}
```

### 親Component

```pug:template(ParentComponent)
.home
  refComponent(ref="refDemo")
```

```ts:script(ParentComponent)
import { Component, Vue } from "vue-property-decorator";
import refComponent from "@/components/ref/Ref.vue";

@Component({
  components: {
    refComponent,
  },
})
export default class Home extends Vue {
  public created() {
    const refDemoComponent: refComponent = this.$refs.refDemo as refComponent;
    console.log(refDemoComponent); // undefined
  }

  public mounted() {
    const refDemoComponent: refComponent = this.$refs.refDemo as refComponent;
    console.log(refDemoComponent);
  }
}
```

## 10. 親Componentから子Componentの関数を呼びたいとき (2019/01/10 追記)

`ref` を使う。
「9. 親Componentから子Componentを識別したいとき」と同じように `ref` を使っていきます。
下記の例では親Componentの `mounted()` のタイミングで、子Componentのメソッドを呼び出して、変数に代入しています。
途中型宣言がうまくできなくてやむなく `any` を使っています…ここきれいに型宣言できる方法知っている方いたら、コメントいただけると幸いです。

### 子Component

```pug:template(ChildComponent)
.ref
  .child-title Ref デモ
```

```ts:script(ChildComponent)
import { Component, Vue, Prop } from "vue-property-decorator";

@Component({})
export default class RefComponent extends Vue {
  public sampleText: string = "Refのデモです";

  public returnText(): string {
    return this.sampleText;
  }
}
```

### 親Component

```pug:template(ParentComponent)
.home
  refComponent(ref="refDemo")
  .ref-demo-text {{ refDemoText }}
```

```ts:script
import { Component, Vue } from "vue-property-decorator";
import refComponent from "@/components/ref/Ref.vue";

@Component({
  components: {
    refComponent,
  },
})
export default class Home extends Vue {
  public refDemoText: string = "";

  public mounted() {
    const refDemoComponent: any = this.$refs.refDemo; // ここの型宣言がうまくできなかった…
    this.refDemoText = refDemoComponent.returnText();
  }
}
```

## 11. Propの変数が更新されたら子Componentも更新したいとき (2019/01/10 追記)

`ref` を使う。
「9. 親Componentから子Componentを識別したいとき」と同じように `ref` を使っていきます。
というよりは「10. 親Componentから子Componentの関数を呼びたいとき」の応用です。
下記の例では、親Componentのテキストボックスの内容(`refInputText`)をpropで子Componentの値(`refText`)に渡していて、 `refInputText` を更新した時に、子Componentの `updateRefText()` を呼び出して `refText` を更新しています。

### 子Component

```pug:template(ChildComponent)
.ref
  .child-title Ref デモ
  .ref-input-text {{ refText }}
```

```ts:script(ChildComponent)
import { Component, Vue, Prop } from "vue-property-decorator";

@Component({})
export default class RefComponent extends Vue {
  @Prop() public refText: string;

  public sampleText: string = "Refのデモです";

  public updateRefText(newText: string) {
    this.refText = newText;
  }
}
```

### 親Component

```pug:template(ParentComponent)
.home
  refComponent(ref="refDemo")
  el-row
    el-col(:span="10")
      el-input(v-model="refInputText" @change="changeRefInputText")
    el-col(:span="14")
      .ref-demo-text {{ refDemoText }}
```

```ts:script(ParentComponent)
import { Component, Vue } from "vue-property-decorator";
import refComponent from "@/components/ref/Ref.vue";

@Component({
  components: {
    refComponent,
  },
})
export default class Home extends Vue {
  public refDemoText: string = "";

  public mounted() {
    const refDemoComponent: any = this.$refs.refDemo;
    this.refDemoText = refDemoComponent.returnText();
  }

  public changeRefInputText() {
    const refDemoComponent: any = this.$refs.refDemo;
    refDemoComponent.updateRefText(this.refInputText);
  }
}
```

## さいごに

これからも「〇〇なときどう書いたっけな？」と困ることがあったら随時アップデートしていこうと思います！
ではまた！！