2011-09-21 11 views
24

ノックアウトマッピングプラグイン(http://knockoutjs.com/documentation/plugins-mapping.html)を使用すると、階層的なオブジェクトをマッピングできますか?ノックアウトマッピングプラグインを使用して階層オブジェクトをカスタムクラスにマッピングする

私は複数のレベルを持つオブジェクトがある場合:

var data = { 
    name: 'Graham', 
    children: [ 
     { 
      name: 'Son of Graham', 
      children: [ 
       { 
        name: 'Son of Son of Graham', 
        children: [ 
         { 
          ... and on and on.... 
         } 
        ] 

       } 
      ] 
     } 
    ] 
} 

をどのように私はJavaScriptで私のカスタムクラスにマッピングします:

var mapping = { 
    !! your genius solution goes here !! 

    !! need to create a myCustomPerson object for Graham which has a child myCustomerPerson object 
    !! containing "Son of Graham" and that child object contains a child myCustomerPerson 
    !! object containing "Son of Son of Graham" and on and on.... 

} 

var grahamModel = ko.mapping.fromJS(data, mapping); 

function myCustomPerson(name, children) 
{ 
    this.Name = ko.observable(name); 
    this.Children = ko.observableArray(children); 
} 

マッピング・プラグインを再帰的にこのデータをマッピングすることができます私のカスタムオブジェクトのhierachy?

答えて

17

サムシング(Live copy on js fiddle):

CSS:

.left { 
    float: left; 
} 

.clear { 
    clear: both; 
}​ 

HTML:

<p>Current:&nbsp; 
    <a href="#" data-bind="visible: (stack.length > 0), text: selectedNode().name, click: selectParentNode"></a> 
    <span data-bind="visible: (stack.length <= 0), text: selectedNode().name"></span> 
</p> 
<p class="left">Children:&nbsp;</p> 
<ul class="left" data-bind="template: {name: 'childList', foreach: selectedNode().children}"></ul> 

<script type="text/html" id="childList"> 
    <li data-bind="click: function(){nodeViewModel.selectChildNode($data)}"> 
     <a href="#">A${name}</a> 
    </li> 
</script> 

<br /><br /> 
<ul class="clear" data-bind="template: {name: 'backBtn'}"></ul> 

<script type="text/html" id="backBtn"> 
    <a href="#" data-bind="visible: $data.selectedNode().back, click: function() { nodeViewModel.selectBackNode($data.selectedNode().back) }">Back</a> 
</script>​ 

はJavaScript:

var node = function(config, parent) { 
    this.parent = parent; 
    var _this = this; 

    var mappingOptions = { 
     children: { 
      create: function(args) { 
       return new node(args.data, _this); 
      } 
     } 
    }; 

    ko.mapping.fromJS(config, mappingOptions, this); 
}; 

var myModel = { 
    node: { 
     name: "Root", 
     children: [ 
      { 
      name: "Child 1", 
      back: 1, 
      children: [ 
       { 
       name: "Child 1_1", 
       back: 1, 
       children: [ 
        { 
        name: "Child 1_1_1", 
        back: 4, 
        children: [ 
         ]}, 
       { 
        name: "Child 1_1_2", 
        back: 2, 
        children: [ 
         ]}, 
       { 
        name: "Child 1_1_3", 
        back: 1, 
        children: [ 
         ]} 
        ]} 
      ]}, 
     { 
      name: "Child 2", 
      back: 1, 
      children: [ 
       { 
       name: "Child 2_1", 
       back: 1, 
       children: [ 
        ]}, 
      { 
       name: "Child 2_2", 
       back: 1, 
       children: [ 
        ]} 
      ]} 
     ] 
    } 
}; 

var viewModel = { 

    nodeData: new node(myModel.node, undefined), 

    selectedNode: ko.observable(myModel.node), 

    stack: [], 

    selectBackNode: function(numBack) { 

     if (this.stack.length >= numBack) { 
      for (var i = 0; i < numBack - 1; i++) { 
       this.stack.pop(); 
      } 
     } 
     else { 
      for (var i = 0; i < this.stack.length; i++) { 
       this.stack.pop(); 
      } 
     } 

     this.selectNode(this.stack.pop()); 
    }, 

    selectParentNode: function() { 
     if (this.stack.length > 0) { 
      this.selectNode(this.stack.pop()); 
     } 
    }, 

    selectChildNode: function(node) { 
     this.stack.push(this.selectedNode()); 
     this.selectNode(node); 
    }, 

    selectNode: function(node) { 
     this.selectedNode(node); 
    } 

}; 

window.nodeViewModel = viewModel; 
ko.applyBindings(viewModel);​ 

このサンプルでは、​​単に無限にマッピングしますJSONデータのネストされたセットであり、実際にこの正確なコードをアプリケーションで使用すると素晴らしいと言えます。

selectBackNodeとselectParentNode

のような余分な機能のいくつかは、ツリーをバックアップに移動することができます。

例をナビゲートしている間、親ラベルは1つのレベルを上げるためのリンクになり、いくつかのリーフノードにはバックボタンがあり、指定されたレベル数だけツリーを上に戻すことができます。

--EDIT--

あなたのリーフ・ノードは、子配列を持っていない場合は、モデル内に存在しない追加データが導入された問題を取得する可能性があります。

+0

これは、HJ05素晴らしい作品です。答えをありがとう。 –

+0

+1スタックadtの使用 – booyaa

+0

jsFiddleのようなリンクは素敵な*付属*ですが、常に関連するコードとマークアップ**を回答自体に入れます**。理由:http://meta.stackexchange.com/questions/118392/add-stack-overfow-faq-entry-or-similar-for-putting-code-in-the-question私はあなたのためにこれでやった機会。 –

4

私の経験から、問題はないはずです。

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

は、その後、あなたのデバッガ(クロムまたはFF + Firebugのは、最高の作品)で生成されたオブジェクトを見に次の行にブレークポイントを設定 -

は、私は次の行を使用します。こうすることで、ko.mappingがあなたのニーズを満たすビューモデルを生成するかどうかを知ることができます。

通常、エンドポイント(値を持つ変数)のみがko.observablesであるオブジェクトを生成します。 ... children: [...のような、データのナビゲーションに使用できる他のデータ時間は、通常のjavaScriptオブジェクトとして表示されます。このような

1

ネストされたmappingOptions(ノードレベルごとにkoマップオブジェクトを作成する)を使用しない場合は、createのkoマッピングオプションによって親オブジェクトにアクセスできるという利点があります。

function Folder(parent,data) { 
    var self = this; 
    self.parent = parent; 
    ko.mapping.fromJS(data, self.map, self); 
} 

Folder.prototype.map = { 
    'folders': { 
     create: function(options) { 
      var folder = new Folder(options.parent,options.data); 
      return folder; 
     } 
    } 
} 

var data = { name:"root", folders: [ {name:"child", folders: [] } ] }; 
var root = new Folder(null, data); 

このようにして、クラスのプロトタイプ内にマップのコピーが1つしかありません(または任意の関数になる可能性があります)。 Folder.parentをobservableにしたい場合は、マップ関数内でdata.parent = parent;を実行し、Folderコンストラクタのパラメータとして渡すことはできません。self.parent = parent;

+0

これは機能します。私は下の私の答えのためにそれを使用しました。 –

0

私はアプローチを使用しましたこのanswerでは、子を持つノードが折りたたみ可能なチェックボックスの階層を作成し、親をチェック/チェック解除すると子ノードのチェック/チェックが解除されます。

ビューモデル

​​

HTML(ビュー)

<div data-role="panel" id="left-panel" data-position="left" data-position-fixed="false" data-theme="b"> 
      <div data-role="collapsible-set" data-bind="template: {name: 'category_collapsible', foreach: sub_categories}" data-mini="true" id="categories" data-iscroll> </div> 
     </div><!-- END left panel --> 

     <script type="text/html" id="category_collapsible"> 
      <div class="category_collapsible" data-mini="true" data-content-theme="b" data-inset="true" data-iconpos="right"> 
       <h3>  
        <input data-role="none" data-them="b" data-bind='checked: categoryChecked, jqmChecked: true, attr: {id: "category_checkbox_"+id}' class="chk_category" type="checkbox" /> 
        <label data-bind='attr: {for: "category_checkbox_"+id}'><span data-bind="text: name"> </span></label> 
       </h3> 
       <ul data-role="listview" data-bind="template: {name: 'category_list', foreach: sub_categories}"> 

       </ul> 
      </div> 
     </script><!-- END category_collapsible template --> 

     <script type="text/html" id="category_list"> 
      <!-- ko if: sub_categories().length==0 --> 
       <li data-theme="c"> 
        <input data-role="none" data-theme="c" data-bind='checked: categoryChecked, jqmChecked: true, attr: {id: "category_checkbox_"+id}' class="chk_category" type="checkbox"/> 
        <label data-corners="false" data-bind='attr: {for: "category_checkbox_"+id}'> 
         <span data-bind="text: name"> </span> 
        </label>   
       </li> 
      <!-- /ko --> 
      <!-- ko if: sub_categories().length>0 --> 
       <li data-theme="c" data-bind="template: {name: 'category_collapsible', data: $data}"></li> 
      <!-- /ko --> 
     </script> 
関連する問題