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?