概要
以下の記事でやり方がわかったので、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://cloudpack.media/43084