Mappingプラグイン(その2)

これはKnockoutJSアドベントカレンダー19日目の記事です。


KnockoutJS Advent Calendar 2014 - Qiita



MappingプラグインはKnockoutJS公式のドキュメントに唯一書かれてあるプラグインです。
http://knockoutjs.com/documentation/plugins-mapping.html


公式ドキュメントには色々書いてありますが、簡単に言うと
「JSのオブジェクト内のプロパティや配列を observable / observableArray に変換」するプラグインです。

前回基本的な使い方を説明したのですが、それだけでは実際に使うのは難しい場面が多いと思うので、
その際に必要になるマッピングオプションを説明しようと思います。

公式ドキュメントを要約した感じなので詳しくはそちらを参照してください。


key, create, update

以下のようなデータがあります。

var data = {
    name: 'Scot',
    children: [
        { id : 1, name : 'Alicw' }
    ]
}
var viewModel = ko.mapping.fromJS(data);

上記のコードにtypoがあるので、以下のコードで上書きをしたいと思いました。

var data = {
    name: 'Scott',
    children: [
        { id : 1, name : 'Alice' }
    ]
}
ko.mapping.fromJS(data, viewModel);

上記のように書くことで name は 'Scott' に変わりましたが、 children は上書きしてほしかったのですが、
上書きされずに一度削除され新しく追加されました。

と公式ドキュメントにはこう書いてあるのですが、挙動が分かりにくいかと思うので

マッピングオプションの create と update も加えてサンプルを使って説明します。

まず先程のサンプルの挙動がどうなるのかを見てみます。

サンプルコード

var data = {
    name: 'Scot',
    children: [
        { id : 1, name : 'Alicw' }
    ]
}
var viewModel = ko.mapping.fromJS(data);
var data = {
    name: 'Scott',
    children: [
        { id : 1, name : 'Alice' }
    ]
}
var mapping = {
    'children': {
        create: function(options) {
            console.log("create");
            return options.data;
        },
        update: function(options) {
            console.log("update");
            return options.data;
        }
    }
}
ko.mapping.fromJS(data, mapping, viewModel);
ko.applyBindings(viewModel);

ログ出力は以下のようになりました。

create
update

これを id を key にして上書きするようにしてみましょう。

<p data-bind="text: name"></p>
<div data-bind="foreach:children">
  <p data-bind="text: id"></p>
  <p data-bind="text: name"></p>
</div>

<script>
var data = {
    name: 'Scot',
    children: [
        { id : 1, name : 'Alicw' }
    ]
}
var viewModel = ko.mapping.fromJS(data);
var data = {
    name: 'Scott',
    children: [
        { id : 1, name : 'Alice' }
    ]
}
var mapping = {
    'children': {
        key: function(data) {
            return ko.utils.unwrapObservable(data.id);
        },
        create: function(options) {
            console.log("create");
            return options.data;
        },
        update: function(options) {
            console.log("update");
            return {id:options.data.id, name:options.data.name};
        }
    }
}
ko.mapping.fromJS(data, mapping, viewModel);
ko.applyBindings(viewModel);
</script>


するとログ出力は以下のようになります。

update

上記のように key を指定することで create を実行せず key の情報をもとに update だけを実行します。



mappedRemove, mappedCreate

公式ドキュメントにありますが、どういう出力結果になるかぱっと見で分からなかったので試してみました。

var obj = [
    { id : 1 },
    { id : 2 }
]
 
var result = ko.mapping.fromJS(obj, {
    key: function(item) {
        return ko.utils.unwrapObservable(item.id);
    },
    create: function(options) {
        console.log("create");
        return options.data;
    },
    update: function(options) {
        console.log("update");
        return options.data;
    },
});
console.log(result().length); // 配列の長さ: 2
console.log(result()[0].id); // 出力結果: 1
console.log(result()[1].id); // 出力結果: 2
result.mappedRemove({ id : 1 });
console.log(result().length); // 配列の長さ: 1
console.log(result()[0].id); // 出力結果: 2

var newItem = result.mappedCreate({ id : 3 });
console.log(result().length); // 配列の長さ: 2
console.log(result()[0].id); // 出力結果: 2
console.log(result()[1].id); // 出力結果: 3
console.log(newItem); // 出力結果: Object {id: 3}


他にも ignore, include, copy, observe や mappedXxx が色々ありますがまた機会があれば。


明日は @yusuke_nozoe さんです!よろしくお願いします!