80
76

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Vue.js のコンポーネント に JSライブラリ を統合する

Last updated at Posted at 2014-07-11

はじめに

Vue.js いいですよ、Vue.js。

シンプルでパワフルです。

Angular(JS)と較べて、学習コストが半端なく低い。直感的にDOM操作を行えます。

同じくMVVMを基盤にするKnockoutJSよりもシンプルに書けます。

日本語の情報が充実しているので、今以上にブレイクする可能性があると思っています。

公式サイトのガイド・APIリファレンスが有志の方のおかげで日本語にも対応しているので、クライアントJSにあかるい方がいれば、即採用可能な状況になっています。

コンポーネント(View Model)へのJSライブラリ組込み

v3.x

  1. JSライブラリをディレクティブとしてラップし、JSライブラリを適用する要素にディレクティブを独自属性として追加する

  2. JSライブラリをディレクティブとしてラップし、JSライブラリを適用する要素にディレクティブを独自属性として追加した上で、デイレクティブを適用した要素の親要素をコンポーネントとしてラップし、コンポーネントを独自タグとして利用する

  3. JSライブラリを適用する要素をコンポーネント
    としてラップし、JSライブラリを適用する要素のかわりにコンポーネントを独自タグとして利用する。場合によっては、更に独自タグの親要素をコンポーネント化する

のどれかで、View ModelにスマートにJSライブラリを統合できます。
v2.xと同じ手法です。

Vue.js の vDOM 実装が書き直され、ディレクティブからView Model (VM) へのアクセスのしかたが変わっています。

vnode.contextではなくbinding.instanceで VM にアクセスします。

v3.xは、v2.x とはディレクティブetcの挙動が多少、異なりますので、注意が必要です。

v2.x

  1. JSライブラリをディレクティブとしてラップし、JSライブラリを適用する要素にディレクティブを独自属性として追加する

  2. JSライブラリをディレクティブとしてラップし、JSライブラリを適用する要素にディレクティブを独自属性として追加した上で、デイレクティブを適用した要素の親要素をコンポーネントとしてラップし、コンポーネントを独自タグとして利用する

  3. JSライブラリを適用する要素をコンポーネントとしてラップし、JSライブラリを適用する要素のかわりにコンポーネントを独自タグとして利用する。場合によっては、更に独自タグの親要素をコンポーネント化する

のどれかで、View ModelにスマートにJSライブラリを統合できます。

v1.xでの DOM に直接アクセスする実装から、v2.xでは、vDOMを介して、DOM にアクセスする実装に変更されたため、v2.x はv 1.x とはディレクティブetcの挙動が異なりますので、注意が必要です。

Vue.js 2.0でtwoWayなカスタムディレクティブを実装する方法

v1.0.x

JSライブラリをディレクティブとしてラップし、JSライブラリを適用する要素にディレクティブを独自属性として追加することでView ModelにスマートにJSライブラリを統合できます。

1.0.xでも、ディレクティブではなくコンポーネントとしてラップする
ことは可能です。

※注意

上記手法を取らず、View Modelの外側から直接JSライブラリを適用することも可能ですが、その場合、v-for (旧v-repeat) 等で動的に追加したDOMノード等に対してJSライブラリを適用できません。

組込み例

jQueryプラグイン bootstrap-tokenfield (Vue1.0まで)、tagEditor (Vue2.0以降)を上記の手法でView Modelに組み込んでいます。

bootstrap-tokenfield

tagEdtior

1. コンポーネント(View Model)への単純な組込み

v3.x

[directive / composition api]

[directive / options api]

v2.x

[directive]

v1.0

<div id="form">
  <input id="text-content" v-tokenfield="content3" type="text">
  <p>
    <button id="btn-change" v-on:click="on_click">Change Content by JS</button>
  </p>
  <p>
    <button id="btn-log" v-on:click="on_click2">Check VM.content</button>
  </p>
  <p>{{content2}}</p>
</div>

v0.10, 0.11

<div id="form">
  <input id="text-content" v-tokenfield="content3" type="text">
  <p>
    <button id="btn-change" v-on="click: on_click()">Change Content by JS</button>
  </p>
  <p>
    <button id="btn-log" v-on="click: on_click2()">Check VM.content</button>
  </p>
  <p>{{content2}}</p>
</div>

v1.0, v0.11

$(function(){
  Vue.directive('tokenfield',{
    twoWay: true,
    bind: function(value){
      var self = this;
      $(this.el).tokenfield();
      $(this.el).on('tokenfield:editedtoken',function (e) {
        self.set($(this).tokenfield("getTokensList", ','));
      }).on('tokenfield:createdtoken', function(e){
        self.set($(this).tokenfield("getTokensList", ','));
      }).on('tokenfield:removedtoken', function(e){
        self.set($(this).tokenfield("getTokensList", ','));
      });
    },
    update: function(value){      
      $(this.el).tokenfield('setTokens',value);
    }
  });
    
  var vm = new Vue({
    el: '#form',
    data: {
      content2: '',
      content3: ''
    },
     methods: {
       on_click: function () {
         this.content3 = 'hi';
       },
       on_click2: function () {
         this.content2 = this.content3;
       }
    }
  });

});

v1.0:
v0.11:

v0.10

$(function(){
  Vue.directive('tokenfield',{
    bind: function(value){
      var self = this;
      $(this.el).tokenfield();
      $(this.el).on('tokenfield:editedtoken',function (e) {
        self.vm.$set(self.key,$(this).tokenfield("getTokensList", ','));
      }).on('tokenfield:createdtoken', function(e){
        self.vm.$set(self.key,$(this).tokenfield("getTokensList", ','));
      }).on('tokenfield:removedtoken', function(e){
        self.vm.$set(self.key,$(this).tokenfield("getTokensList", ','));
      });
    },
    update: function(value){
      $(this.el).tokenfield('setTokens',value);
    }
  });
    
  var vm = new Vue({
    el: '#form',
    data: {
      content2: '',
      content3: ''
    },
     methods: {
       on_click: function () {
          this.content3 = 'hi';
       },
       on_click2: function () {
         this.content2 = this.content3;
       }
    }
  });

});

2. v-for (旧v-repeat) のある コンポーネント(View Model)への組込み

v3.x

[directive+component / composition api]

[directive+component / options api]

v2.x

[directive+component]

v1.0

<div id="form">
    <div v-for="row in contents">
      <input  type="text" class="tag-editor" v-tokenfield="row.content3" >
      <p>
        <button id="btn-log" v-on:click="on_click2(row)">Check VM.content</button>
      </p>
        <p>{{row.content2}}</p>
    </div>
    <p>
      <button id="btn-change" v-on:click="on_click">Add Content by JS</button>
    </p>
</div>
</div>

v1.0, v0.11

$(function(){  
  Vue.directive('tokenfield',{
    twoWay: true,
    bind: function(value){
      var self = this;
      $(this.el).tokenfield();
      $(this.el).on('tokenfield:editedtoken',function (e) {
        self.set($(this).tokenfield("getTokensList", ','));
      }).on('tokenfield:createdtoken', function(e){
        self.set($(this).tokenfield("getTokensList", ','));
      }).on('tokenfield:removedtoken', function(e){
        self.set($(this).tokenfield("getTokensList", ','));
      });
    },
    update: function(value){      
      $(this.el).tokenfield('setTokens',value);
    }
  });
    
  var vm = new Vue({
    el: '#form',
    data: {
      contents: [
        {
          content2: '',
          content3: 'wi'
        },
        {
          content2: '',
          content3: 'fi'
        }
      ]
    },
     methods: {
       on_click: function () {
          vm.contents.push({
            content2:'',
            content3:''
          });
       },
       on_click2: function(row){
         row.content2 = row.content3;
       }
    }
  });
  
});

v1.0:
v0.11:

$(function(){  
  Vue.directive('tokenfield',{
    bind: function(value){
      var self = this;  
      $(this.el).tokenfield();
      $(this.el).on('tokenfield:editedtoken',function (e) {
        self.vm.$set(self.key,$(this).tokenfield("getTokensList", ','))
      }).on('tokenfield:createdtoken', function(e){
        self.vm.$set(self.key,$(this).tokenfield("getTokensList", ','))
      }).on('tokenfield:removedtoken', function(e){
        self.vm.$set(self.key,$(this).tokenfield("getTokensList", ','))
      });
    },
    update: function(value){
      $(this.el).tokenfield('setTokens',value);
    }
  });
    
  var vm = new Vue({
    el: '#form',
    data: {
      contents: [
        {
          content2: '',
          content3: 'wi'
        },
        {
          content2: '',
          content3: 'fi'
        }
      ]
    },
     methods: {
       on_click: function () {
          vm.contents.push({
            content2:'',
            content3:''
          });
       },
       on_click2: function (row) {
         row.content2 = row.content3;
       }
    }
  });
  
});

##最後に
組込み例の詳しい解説は書きません。
簡単なので、頑張って?理解してください。

レッツ・トライ、Vue.js。

80
76
1

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
80
76

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?