Edited at

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

More than 1 year has passed since last update.


概要

以下の記事でやり方がわかったので、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>
(略)



確認

ではブラウザで確認してみましょう。

テキストボックスの内容を変更すると、ちゃんと反映されると思います。

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