Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
25
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

Organization

Vue.js+TypeScriptでコンポーネントをv-modelでつなげてみる

概要

以下の記事でやり方がわかったので、TypeScriptでどう書くかまとめてみました。

vueでv-modelをネストして使いたい
https://qiita.com/xsota/items/010e7116c895b782b7a4

準備

Vue-Cliで環境を作ります。

GitHubに利用したプロジェクトをUPしています。実際に試してみたい方どうぞ^^
https://github.com/kai-kou/vue-js-typescript-nest-v-model

ここではDockerを利用して環境構築していますが、ローカルで構築してもらってもOKです。

> mkdir 任意のディレクトリ
> cd 任意のディレクトリ
> vi Dockerfile
> vi docker-compose.yml
Dockerfile
FROM node:10.8.0-stretch

RUN npm install --global @vue/cli

WORKDIR /projects
docker-compose.yml
version: '3'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    volumes:
      - ".:/projects"
    tty: true
> docker-compose up -d
> docker-compose exec app bash
コンテナ内
> vue create app

Vue CLI v3.0.1
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, TS, Linter
? Use class-style component syntax? Yes
? Use Babel alongside TypeScript for auto-detected polyfills? Yes
? Pick a linter / formatter config: TSLint
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No
? Pick the package manager to use when installing dependencies: (Use arrow keys)
❯ Use Yarn
  Use NPM
コンテナ内
> cd app
> yarn serve

これで実装の準備が整いました。

親コンポーネントに子コンポーネントを追加する

今回はVue-Cliでプロジェクト作成時に含まれているHelloWorldコンポーネントに子コンポーネントを足してみます。

> touch src/components/ChildComponent.vue
> vi src/components/ChildComponent.vue
src/components/ChildComponent.vue
<template>
  <input type="text" v-model="localValue"/>
</template>

<script lang="ts">
  import {
    Component,
    Emit,
    Prop,
    Vue,
  } from 'vue-property-decorator';

  @Component
  export default class ChildComponent extends Vue {
    @Prop() public value!: string;

    @Emit()
    public input(value: string) {}

    private get localValue(): string {
      return this.value;
    }

    private set localValue(value: string) {
      this.input(value);
    }
  }
</script>

親から受け取るvalue を直接v-model に指定できないので、get、setプロパティを間に挟んで、set時にEmit してあげるわけですね。
へぇ。わかれば簡単。

注意点はvalue プロパティとinput メソッド名を変更するとだめ。ってところでしょうか。

Vue.js:v-modelと$emitを使ってデータを読み書きする子コンポーネントをつくる
https://qiita.com/ozone/items/b75efe5c449cbc469b1e

公式で以前読んだはずなのにすっかり忘れていたのですが、v-modelは実はただの糖衣構文。:value(prop)と@input(event)に展開して扱われます。

だそうです。

では、HelloWorldコンポーネントも変更します。
msg プロパティをv-model で取り扱えるようにvalue に変更します。
こちらもvalue を直接v-model に設定せず、get、setプロパティを間に挟みます。

src/components/HelloWorld.vue
<template>
  <div class="hello">
    <child-component v-model="localValue"/>
    <h1>{{ value }}</h1>
    (略)
  </div>
</template>

<script lang="ts">
import { Component, Emit, Prop, Vue } from 'vue-property-decorator';
import ChildComponent from './components/ChildComponent.vue';

@Component({
  components: { ChildComponent },
})
export default class HelloWorld extends Vue {
  @Prop() private value!: string;

  @Emit()
  public input(value: string) {}

  private get localValue(): string {
    return this.value;
  }

  private set localValue(value: string) {
    this.input(value);
  }
}
</script>
(略)

最後にApp.vueを変更します。HelloWorldコンポーネントのmsg プロパティがなくなったので、v-model に変更します。

src/App.vue
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld v-model="msg"/>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from './components/HelloWorld.vue';

@Component({
  components: {
    HelloWorld,
  },
})
export default class App extends Vue {
  private msg = 'Welcome to Your Vue.js + TypeScript App';
}
</script>
(略)

確認

ではブラウザで確認してみましょう。
テキストボックスの内容を変更すると、ちゃんと反映されると思います。
スクリーンショット 2018-08-28 17.54.18.png

get、setプロパティを挟むのが手間ですが、まあまあ良いのではないでしょうか。

参考

vueでv-modelをネストして使いたい
https://qiita.com/xsota/items/010e7116c895b782b7a4

Vue.js:v-modelと$emitを使ってデータを読み書きする子コンポーネントをつくる
https://qiita.com/ozone/items/b75efe5c449cbc469b1e

Vue.js+TypeScriptで開発するときの参考記事まとめ
https://qiita.com/kai_kou/items/19b494a41023d84bacc7

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
25
Help us understand the problem. What are the problem?