コンポーネントのデータをコピペする方法

Sencha Ext JS には AbstractClipboard というクラスがあり、それを拡張して、コンポーネントのデータをコピペするプラグインを作ることができます。

以下、Sencha のブログで紹介された、タグフィールドでコピペできるようにする例です。

### コンポーネントのデータをコピペする方法

*by Alex Volfson*

ユーザーからコンポーネントのデータをコピペする方法を聞かれます。多くのコンポーネントには複雑なインタラクションモデルがあるので、システムデフォルトのコピーでは動作するとは限りません。クリップボードプラグインを使うのは難しそうに見えますが、タグフィールド コンポーネントに実装する方法をご覧に入れましょう。選択されたタグだけをコピペするにはどうしたらいいかを説明します。

**Ext.plugin.AbstractClipboard** はデフォルトで2つのメソッドを期待します。1つ目は **getTextData** で、コンポーネントからシステムのクリップボードにコピーしたいデータを特定するためのものです。
2つ目のメソッドは **putTextData** で、こちらはコンポーネントがペーストされたときに処理する動作を指定します。
以上を念頭に、**AbstractClipboard** を拡張して **tagfield** コンポーネント用に動作するようにしたのが次のコードです。

```javascript
Ext.define('myApp.plugins.TagsClipboard', {
    extend: 'Ext.plugin.AbstractClipboard',
    alias: 'plugin.tagclipboard',

    separator:", ", //システムのクリップボードにコピーされる時に値を区切る記号を定義します

    /*
     * コピー／切り抜きでクリップボードへのデータの送り方を定義します
     */
    getTextData:function(){
        var cmp=this.getCmp(); // これはタグフィールド
        var selected = cmp.selectionModel.getSelected(); // 選択されているものを取得
        var str = "";
        for (var x = 0, len = selected.items.length; x < len; x++) {
            str += selected.items[x].get(cmp.displayField) + this.separator
        }
        str = str.slice(0, - this.separator.length);
        return str;
    },

    /*
     * ペーストをコンポーネントが処理する方法を定義します
     */
    putTextData:function(data,format){
        var arrayValues = data.split(this.separator);
        var cmp= this.getCmp(),rec, records=[];
        // それぞれの文字列を照合して後で setValue を使います
        for(var x=0, len=arrayValues.length; x<len; x++){
           rec=cmp.getStore().findRecord(cmp.displayField,arrayValues[x],0,false,false,true);
           if (rec) {
                records.push(rec);
           }
        }

        var combined = records.concat(cmp.getValueRecords());// 既存のレコードとまとめます

        cmp.setValue(combined);

        return data;
    },

    privates:{

         finishInit: function (comp) {
            var me = this;

            me.keyMap = new Ext.util.KeyMap({
                target: me.getTarget(comp),
                //ignoreInputFields: true, // このためにオーバーライドしています
                binding: [{
                    ctrl: true, key: 'x', fn: me.onCut, scope: me
                }, {
                    ctrl: true, key: 'c', fn: me.onCopy, scope: me
                }, {
                    ctrl: true, key: 'v', fn: me.onPaste, scope: me
                }]
            });

            ++me.shared.counter;

            comp.on({
                destroy: 'destroy',
                scope: me
            });
        },
    }

})
```

> 訳注: 上記に **finishInit** がありますが、これは **AbstractClipboard** に元々あるメソッドでそれをオーバーライドしています。**AbstractClipboard** では、KeyMap の ignoreInputFields を true に設定していますが、多義フィールドは入力フィールドなので、このままだとキーイベントを拾うことができません。そこで、 `ignoreInputFields: true` としているわけです。

さらに **finishinit** 内でペーストのリスナーを削除して、ブラウザーに処理させてもいいでしょう。そうした場合の唯一の問題は、タグフィールドが一つのタグしか処理できないことだけです。クリップボードに複数のタグがある場合は、コンポーネントはそれを処理する方法を知りません。これで残りは、このプラグインを**tagfield** コンポーネントに追加するだけです。

```javascript
       var shows = Ext.create('Ext.data.Store', {
            fields: ['id', 'show'],
            data: [{
                id: 0,
                show: 'Battlestar Galactica'
            }, ...]
        });

        Ext.create('Ext.form.field.Tag', {
            plugins:[{ptype:'tagclipboard',separator:' && '}],
            fieldLabel: 'Select a Show',
            store: shows,
            displayField: 'show',
            valueField: 'id',
            queryMode: 'local',
            filterPickList: true,

        });
```

プラグインのインスタンス毎にセパレーターを定義できます。それでは、コピペを楽しんでください。

------------

元ネタ: [Top Support Tips | Sencha](https://www.sencha.com/blog/top-support-tips-june-2016/)