Knockout.js 日本語ドキュメント

Mapping プラグイン

Knockout は、任意の JavaScript オブジェクトをビューモデルとして使用できるように設計されています。ビューモデルのプロパティのうちいくつかが observable であれば、 Knockout はそれらのプロパティと UI をバインドすることができ、 observable なプロパティが変更されるたびに UI は自動的に更新されます。

ほとんどのアプリケーションは、バックエンドサーバからデータを取得する必要があります。サーバは observable についていかなる概念も持っていないので、生の JavaScript オブジェクト(通常は JSON としてシリアライズされます)を供給するのみです。mapping プラグインは、生の JavaScript オブジェクトを、適切な observable を持つビューモデルにマッピングする簡単な方法を提供します。これは、サーバから取得したなんらかのデータに基づいたビューモデルを構成するために、手動で独自の JavaScript コードを書く方法の代替手段です。

ダウンロード

Version 2.0 (8.6kb minified)

例: ko.mapping プラグイン無しでの手動マッピング

たとえば現在のサーバー時刻とウェブページ上のユーザー数を表示したいとします。以下のビューモデルを使用してこの情報を表すことができます:

var viewModel = {
    serverTime: ko.observable(),
    numUsers: ko.observable()
}

以下のように、このビューモデルをなんらかの HTML 要素に対してバインドすることができます:

The time on the server is: <span data-bind='text: serverTime'></span>
and <span data-bind='text: numUsers'></span> user(s) are connected.

ビューモデルのプロパティは observable であるため、Knockout はそれらのプロパティが変更されるたびに、自動的に HTML 要素を更新します。

次に、サーバから最新のデータを取得します。 例として5秒ごとに Ajax リクエストを発行します(例えば、 jQuery の $.getJSON または$.ajax 関数を使用します)。

var data = getDataUsingAjax();          // Gets the data from the server

サーバは以下のような JSON データを返します。

{
    serverTime: '2010-01-07',
    numUsers: 3
}

最後に、このデータを使用して(mapping プラグインを使用せずに)ビューモデルを更新するため、以下のように記述します。

// 毎回サーバからデータを受け取ります:
viewModel.serverTime(data.serverTime);
viewModel.numUsers(data.numUsers);

ページに表示するすべての変数に対して、これを行うことになります。子要素や配列が含まれたりデータ構造がより複雑になれば、手動で処理するのが非常に煩雑になります。そこで mapping プラグインによって、通常の JavaScript オブジェクト(または JSON 構造)から observable なビューモデルへのマッピングを作成することが可能になります。

例: ko.mapping の使用

mapping プラグインによってビューモデルを作成するには、上記のコードについて viewModel の作成を ko.mapping.fromJS 関数で置き換えます。

var viewModel = ko.mapping.fromJS(data);

これは data の各プロパティについて、自動的に observable なプロパティを作成します。その後、サーバから新しいデータを受け取るたびに ko.mapping.fromJS 関数を再び呼び出すことによって、ワンステップで viewModel 上の全てのプロパティを更新できます。

// data は毎回サーバから受け取ります:
ko.mapping.fromJS(data, viewModel);

どのようにマッピングされるのか

  • オブジェクトの全てのプロパティは observable に変換されます。更新によって値が変更されると、それは observable な形で更新されます。
  • 配列は observable array に変換されます。更新によって配列のいくつかの値が変更されると、適切な add / remove アクションが実行されます。また、元の JavaScript 配列と同じように並び順を維持しようと試みます。

マッピングの解除

もしすでにマッピングされたオブジェクトを、元の、通常の JavaScript オブジェクトに変換したい場合は、以下の関数を使用します。

var unmapped = ko.mapping.toJS(viewModel);

この関数は、マッピング済みオブジェクトのプロパティのうち、元の JavaScript オブジェクトの一部であったもののみを含む、マッピングが解除されたオブジェクトを作成します。言い換えると、手動でビューモデルに追加されたプロパティまたは関数は無視されます。デフォルトでは、このルールの唯一の例外は _destroy プロパティが追加されることです。_destroyko.observableArray から項目を破棄する際に Knockout が生成するプロパティです。これを設定する方法の詳細については、"高度な使い方" を参照してください。

JSON 文字列での動作

Ajax の結果が JSON 形式である(そしてそれを JavaScript オブジェクトにデシリアライズしない)場合は、ビューモデルを作成および更新するため、代わりに ko.mapping.fromJSON 関数を使用することができます。解除には、 ko.mapping.toJSON を使用することができます。

JavaScript オブジェクトの代わりに JSON 文字列を使用することを除けば、これらの関数は *JS の対応する関数と完全に同一です。

高度な使い方

場合によっては、マッピングが実行される方法をより詳細に制御する必要があるかもしれません。この目的はマッピングオプションを使用することで達成可能です。これらのオプションは ko.mapping.fromJS を呼び出す際に指定することができます。それ以降の呼び出しでは、それらを再度指定する必要はありません。

ここではマッピングオプションを使用したいと感じる状況について、いくつかの例を挙げます。

"key"を使用して、一意にオブジェクトを識別する

例えば、以下のような JavaScript オブジェクトを扱っているとしましょう:

var data = {
    name: 'Scot',
    children: [
        { id : 1, name : 'Alicw' }
    ]
}

特に問題なく、これをビューモデルにマッピングすることができます:

var viewModel = ko.mapping.fromJS(data);

そして、ユーザーによりデータが何のタイプミスもない形に更新されたとしましょう:

var data = {
    name: 'Scott',
    children: [
        { id : 1, name : 'Alice' }
    ]
}

ここでは二つの事が起こっています: nameScot から Scott に変更され、 children[0].nameAlicw から、タイプミスのない Alice に変更されました。この新しいデータに基づいて、 viewModel を更新できます。

ko.mapping.fromJS(data, viewModel);

結果として、 name は期待どおりに変更されるでしょう。しかしながら、 children の配列からは子供(Alicw)が完全に削除され、新しい子供(Alice)が加えられました。これは完全に期待していた挙動ではありません。子供そのものを置き換えるのではなく、 name プロパティのみが Alicw から Alice に更新してほしいのです!

このことが起こる理由として、デフォルトでは、mapping プラグインは単純に配列内の2つのオブジェクトを比較します。そして、 JavaScript においてはオブジェクト {ID: 1、name: 'Alicw'}{ID: 1、name: 'Alice'} と等しくないため、子供自体が削除され、新しいものと置き換えられる必要があると判断されます。

これを解決するには、mapping プラグインに対して、オブジェクトの新旧を判断するために使用すべき key を指定することができます。以下のように設定できます:

var mapping = {
    'children': {
        key: function(data) {
            return ko.utils.unwrapObservable(data.id);
        }
    }
}
var viewModel = ko.mapping.fromJS(data, mapping);

これによって、mapping プラグインは children 配列内の項目をチェックするたび、オブジェクト全体を置き換えるか、それとも単に更新が必要なのかを判断するために id プロパティのみを参照します。

"create"を使用して、オブジェクト構築をカスタマイズする

マッピングの一部を自分で操作したい場合、create コールバックを提供することができます。 このコールバックが存在する場合、マッピングの一部をこちらでハンドリングすることができます。

例えば以下のようなJavaScriptオブジェクトを扱っているとしましょう:

var data = {
    name: 'Graham',
    children: [
        { id : 1, name : 'Lisa' }
    ]
}

children の配列を自分でマッピングしたい場合は、以下のように指定することができます。

var mapping = {
    'children': {
        create: function(options) {
            return new myChildModel(options.data);
        }
    }
}
var viewModel = ko.mapping.fromJS(data, mapping);

create コールバックに渡される options 引数は、以下を含むJavaScriptオブジェクトです: * data: この子要素のためのデータを含む JavaScript オブジェクト * parent: この子要素が属する親のオブジェクトまたは配列

もちろん、もし望むのであれば、 create コールバックの内側で別の ko.mapping.fromJS 呼び出しを行うこともできます。典型的なユースケースとしては、元のJavaScriptオブジェクトに対して、他にいくつか computed observable を追加したい場合等でしょう。

var myChildModel = function(data) {
    ko.mapping.fromJS(data, {}, this);

    this.nameLength = ko.computed(function() {
        return this.name().length;
    }, this);
}

"update"を使用して、オブジェクトの更新をカスタマイズする

update コールバックで指定することにより、オブジェクトがどのように更新されるかをカスタマイズすることもできます。これは更新対象のオブジェクトと、create コールバックで使用されるものと同じ、 options オブジェクトを受け取ります。また更新された値を return する必要があります。

update コールバックに渡される options 引数は、以下を含む JavaScript オブジェクトです: * data: この子要素のためのデータを含む JavaScript オブジェクト * parent: この子要素が属する親のオブジェクトまたは配列 * observable: もしプロパティがobservableである場合、こちらに実際の observable が設定されます。

以下は、更新前の入力データにいくつかテキストを追加する設定例です。

var data = {
    name: 'Graham',
}

var mapping = {
    'name': {
        update: function(options) {
            return options.data + 'foo!';
        }
    }
}
var viewModel = ko.mapping.fromJS(data, mapping);
alert(viewModel.name());

ダイアログにて Grahamfoo と表示されます!

"ignore" を使用して特定のプロパティを無視する

mapping プラグインに対し JavaScript オブジェクトのいくつかのプロパティを無視させたい(つまり、それらをマッピングしない)場合、無視するプロパティ名の配列を指定できます。

var mapping = {
    'ignore': ["propertyToIgnore", "alsoIgnoreThis"]
}
var viewModel = ko.mapping.fromJS(data, mapping);

マッピングオプションで指定した ignore 配列は、デフォルトの ignore 配列に結合されます。デフォルトの配列は以下のように書き換えることができます:

var oldOptions = ko.mapping.defaultOptions().ignore;
ko.mapping.defaultOptions().ignore = ["alwaysIgnoreThis"];

“include” を使用して特定のプロパティを含める

ビューモデルを元の JavaScript オブジェクトに変換する場合、デフォルトで元のビューモデルに含まれていたプロパティのみを含めます。ただし例外として、Knockout によって生成された _destroy プロパティは元のオブジェクトの一部でない場合にも含まれます。これは以下のようにカスタマイズすることができます。

var mapping = {
    'include': ["propertyToInclude", "alsoIncludeThis"]
}
var viewModel = ko.mapping.fromJS(data, mapping);

マッピングオプションで指定した include 配列は、最初は _destroy のみが含まれる、デフォルトの include 配列に結合されます。デフォルトの配列は以下のように書き換えることができます:

var oldOptions = ko.mapping.defaultOptions().include;
ko.mapping.defaultOptions().include = ["alwaysIncludeThis"];

"copy" を使用して特定のプロパティをコピーする

ビューモデルを元の JavaScript オブジェクトに変換する場合、デフォルトでは、mapping プラグインは上記で説明したルールに基づいて observable を作成します。プロパティに対してobservable にするのではなく単にコピーするよう mapping プラグインに強制したい場合、"copy" 配列にそのプロパティ名を追加します:

var mapping = {
    'copy': ["propertyToCopy"]
}
var viewModel = ko.mapping.fromJS(data, mapping);

マッピングオプション内で指定した copy 配列は、最初は空である、デフォルトの copy 配列に結合されます。デフォルトの配列は以下のように書き換えることができます:

var oldOptions = ko.mapping.defaultOptions().copy;
ko.mapping.defaultOptions().copy = ["alwaysCopyThis"];

“observe” を使用して特定のプロパティのみを observable 化する

JavaScript オブジェクトのいくつかのプロパティの observable のみを作成して残りをコピーさせたい場合、observable にするプロパティ名の配列を指定することができます。

var mapping = {
    'observe': ["propertyToObserve"]
}
var viewModel = ko.mapping.fromJS(data, mapping);

マッピングオプションで指定した observe 配列は、最初は空であるデフォルトの observe 配列と結合されます。デフォルトの配列は以下のように書き換えることができます:

var oldOptions = ko.mapping.defaultOptions().observe;
ko.mapping.defaultOptions().observe = ["onlyObserveThis"];

ignoreinclude 配列は通常通りに動作します。copy 配列は、子要素に含まれる配列やオブジェクトのプロパティをコピーするために、効果的に使用することができます。配列やオブジェクトのプロパティが copy または observable として指定されていない場合、それは再帰的にマッピングされます。

var data = {
    a: "a",
    b: [{ b1: "v1" }, { b2: "v2" }]
};

var result = ko.mapping.fromJS(data, { observe: "a" });
var result2 = ko.mapping.fromJS(data, { observe: "a", copy: "b" }); //より高速にマッピングされる.

resultresult2 はそれぞれ次のようになります。

{
    a: observable("a"),
    b: [{ b1: "v1" }, { b2: "v2" }]
}

配列/オブジェクトの子孫要素へのマッピングは動作しますが、copyobserve は競合する可能性があります:

var data = {
    a: "a",
    b: [{ b1: "v1" }, { b2: "v2" }]
};
var result = ko.mapping.fromJS(data, { observe: "b[0].b1"});
var result2 = ko.mapping.fromJS(data, { observe: "b[0].b1", copy: "b" });

result は次のようになります。

{
    a: "a",
    b: [{ b1: observable("v1") }, { b2: "v2" }]
}

そして、result2 は次のようになります。

{
    a: "a",
    b: [{ b1: "v1" }, { b2: "v2" }]
}

更新対象の指定

もし上記の例のようにクラスの内部でマッピングを実行する場合、this をマッピング操作の対象にしたいかもしれません。 ko.mapping.fromJS の三番目のパラメータは対象を示しています。例えば、

ko.mapping.fromJS(data, {}, someObject); // someObject のプロパティを上書きします

したがってJavaScript オブジェクトを this にマッピングしたいのであれば、this を三番目の引数として渡すことができます。

ko.mapping.fromJS(data, {}, this);

複数のソースからのマッピング

複数の ko.mapping.fromJS 呼び出しを適用することで、一つのビューモデルに対して複数の JavaScript オブジェクトを組み合わせることができます。例えば:

var viewModel = ko.mapping.fromJS(alice, aliceMappingOptions);
ko.mapping.fromJS(bob, bobMappingOptions, viewModel);

それぞれの呼び出しで指定したマッピングオプションはマージされます。

マッピングされた observable array

mappingプラグインによって生成された observable array は、key マッピングを使用可能な、いくつかの関数で拡張されています。

  • mappedRemove
  • mappedRemoveAll
  • mappedDestroy
  • mappedDestroyAll
  • mappedIndexOf

これらは、通常の ko.observableArray の関数と機能的には同等ですが、オブジェクトのキーに基づいて実行することができます。例えば、以下のように動作します:

var obj = [
    { id : 1 },
    { id : 2 }
]

var result = ko.mapping.fromJS(obj, {
    key: function(item) {
        return ko.utils.unwrapObservable(item.id);
    }
});

result.mappedRemove({ id : 2 });

また、マッピングされた observable array は mappedCreate 関数を公開します。

var newItem = result.mappedCreate({ id : 3 });

この関数は最初にキーをチェックして、キーが既に存在する場合は例外を発生させます。次に、新しいオブジェクトを作成するために、create および update コールバックが存在すれば実行します。最後に、このオブジェクトを配列に追加し、それを返します。

ダウンロード

Version 2.0 (8.6kb minified)

原文はこちら

side menu