LoginSignup
2
2

More than 5 years have passed since last update.

Vue.jsにはAngularのng-changeに相当するディレクティブがないので自分でv-changeを用意してみる

Last updated at Posted at 2015-08-12

コード

v-change.coffee
# CoffeeScriptで御免

Vue.directive 'change',
    acceptStatement: true
    priority: 1024
    update: (fn)->
        model = @el.getAttribute Vue.config.prefix + 'model'
        @handler = ((v)->
            cache = @vm.$value
            if model
                @vm.$value = v
            else
                @vm.$value = @el.value
            fn @vm.$value
            @vm.$value = cache
        ).bind this

        if model
            @vm.$watch model, @handler
        else
            @el.addEventListener 'input', @handler

    unbind: ->
        @el.removeEventListener 'input', @handler

ちゃんと使うならタグの制限やtype属性のチェックが入るけど、最低限の骨組みはこんな感じ。

使い方

vm.coffee
new Vue
    template: ...
    methods:
        func: (v)->
            console.log v

なVMで

<input type="text" v-model="hoge" v-change="func(hoge)">

とか

<input type="text" v-change="func($value)">

とか。$valuev-on$eventみたいに使える。

解説

v-modelとの併用

v-modelが与えられている場合は$watchを使うようにしている。inputイベントはモデルの値が更新される前に発生するので、v-modelがある場合でもinputイベントからコールバックを実行してしまうと前回の値が使われてしまうからである。またコールバックに渡す値はnumber属性を一緒に使うときなど、@el.valueが必ずしもv-modelでバインドされた値と一致しない場合があるので、v-modelがある場合は$watchで呼び出された引数から引っ張っている。

priorityについて

priorityはディレクティブ評価の優先度を設定する。高い方が先に評価される。v-modelのpriority800で、デフォルトは0である。ドキュメントのどこにも書いてないけど、priority
v-modelはこことデフォルトはここで確認できる。

Vue.jsのディレクティブではAngularとちがって評価後は要素から属性が取り除かれてしまうため、v-modelを読み取ろうと思ったらpriority > 800にする必要がある。

$eventみたいなローカルスコープっぽいやつ($value)の作り方

行儀悪いように見えるけど、単純にv-onのソースでやってることの流用である。$eventは特殊なことをしてると思っていたのだが、このやり方で実現してるのを確認したときは少々面食らった。ただ、もともと内側からは$で始まるモデルが作れないの対し外部からは普通に書き込みできるので、一瞬だけ埋め込んで外すというやり方は全てが同期的に行われる限り合理的ではある。

動機など

基本的にVue.jsではインタラクティブな要素に直接ハンドラを与えて変更を拾うようなディレクティブは存在しないので、vm.$watchやComputed propertyのsetterを用いるしかない。

変更の通知のためだけにモデルを用意してvm.$watchをVMの実装から呼ぶなんて到底まとな人間の所業ではないし、Computed propertyにしてもv-modelで使うにはgetterが必要で、状態の保存のためは結局モデルを用意しないといけないし、しかもそのモデルの名前はComputed propertyとは別にしないといけないので、命名で気を揉んで辛いってのが何よりの動機。

あとは「methodsに直接変更のハンドラを書ける必要がある」という意識もある。

Vue.jsの開発周りの議論などを詳しく追っかけてないんで、なんで実装されてないのかわからないが、何か深いわけがあってv-changeをビルトインで持っていないのであれば理由を知りたいところである。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2