2017-05-03 11 views
0

グリッドにグループ製品オプションのネストされた配列があります。グループ化された各製品オプションのすべての製品(製品オプション)をリストし、ユーザーがそれらの関係をチェックできるようにするポップアップエディタを用意したいと思います。私は多対多の関係の例を見てきましたが、多対多にグループ化された自己参照の例は見られませんでした。Knockoutjs - 多数参照リストを多数参照するネストされたグループ配列

は、以下の配列を考慮してください

[{ 
grouptitle: "User Band", 
productoptionrows: [{ 
    id: "1", 
    producttitle: "25-100", 
    relatedproductoptionrows: [{ 
     id: "4", 
     title: '1 Year' 
    }, { 
     id: "5", 
     title: '2 Year' 
    }, { 
     id: "6", 
     title: '3 Year' 
    }] 
}] 

ユーザがタイトルでグループを定義し、そのグループに製品のリストを追加することができるであろう。ユーザがすべてのグループおよび関連製品を追加すると、ユーザはポップアップボタン(「ルックアップ」)をクリックして、各グループの製品間の関係をチェックすることができる。

私の問題はポップアップで、各製品オプションで関係を設定するために「Lookup」をクリックして、リレーションが既にチェックされるようにポップアップをデフォルト設定します。私は自分の問題の根源は複数のネストされた配列を組み合わせようとしているが、このロジックに対処するためにビューモデル/データをどのように構造化するかはわからないと思う。私はその下にシオマネキを設定している

以下の私の問題を示しています。私は本当に誰かが私が持っているように、この作業を達成し、取得しようとして何イム理解することができます願ってい

/*Select Options*/ 
 
var initialData = [{ 
 
    grouptitle: "User Band", 
 
    productoptionrows: [{ 
 
     id: "1", 
 
     producttitle: "25-100", 
 
     relatedproductoptionrows: [{ 
 
      id: "4", 
 
      producttitle: '1 Year' 
 
     }, { 
 
      id: "5", 
 
      producttitle: '2 Year' 
 
     }, { 
 
      id: "6", 
 
      producttitle: '3 Year' 
 
     }] 
 
    }, { 
 
     id: "2", 
 
     producttitle: "101-250", 
 
     relatedproductoptionrows: [{ 
 
      id: "7", 
 
      producttitle: '1 Year' 
 
     }, { 
 
      id: "8", 
 
      producttitle: '2 Year' 
 
     }, { 
 
      id: "9", 
 
      producttitle: '3 Year' 
 
     }] 
 
    }, { 
 
     id: "3", 
 
     producttitle: "251-500", 
 
     relatedproductoptionrows: [{ 
 
      id: "10", 
 
      producttitle: '1 Year' 
 
     }, { 
 
      id: "11", 
 
      producttitle: '2 Year' 
 
     }, { 
 
      id: "12", 
 
      producttitle: '3 Year' 
 
     }] 
 
    }] 
 
}, { 
 
    grouptitle: "Please select the number of years license", 
 
    productoptionrows: [{ 
 
     id: "4", 
 
     producttitle: "1 Year", 
 
     relatedproductoptionrows: [] 
 
    }, { 
 
     id: "5", 
 
     producttitle: "2 Year", 
 
     relatedproductoptionrows: [] 
 
    }, { 
 
     id: "6", 
 
     producttitle: "3 Year", 
 
     relatedproductoptionrows: [] 
 
    }, { 
 
     id: "7", 
 
     producttitle: "1 Year", 
 
     relatedproductoptionrows: [] 
 
    }, { 
 
     id: "8", 
 
     producttitle: "2 Year", 
 
     relatedproductoptionrows: [] 
 
    }, { 
 
     id: "9", 
 
     producttitle: "3 Year", 
 
     relatedproductoptionrows: [] 
 
    }, { 
 
     id: "10", 
 
     producttitle: "1 Year", 
 
     relatedproductoptionrows: [] 
 
    }, { 
 
     id: "11", 
 
     producttitle: "2 Year", 
 
     relatedproductoptionrows: [] 
 
    }, { 
 
     id: "12", 
 
     producttitle: "3 Year", 
 
     relatedproductoptionrows: [] 
 
    }] 
 
}]; 
 

 

 
$(document).ready(function() { 
 
    /*Models*/ 
 
    var mappingOptions = { 
 
     'productoptionrows': { 
 
      create: function (options) { 
 
       return new productoptionrow(options.data); 
 
      } 
 
     } 
 
    }; 
 
    var mappingOptionsPR = { 
 
     create: function (options) { 
 
      return new productoptionrow(options.data); 
 
     } 
 
    }; 
 
    var productoptionrow = function (por) { 
 
     var self = ko.mapping.fromJS(por, {}, this); 
 
     self.relatedproductoptionrowscsv = ko.computed(function() { 
 
      return $(por.relatedproductoptionrows).map(function() { 
 
       return this.id; 
 
      }).get().join(','); 
 
     }, self); 
 
     self.selectedrelatedproductoptionrows = ko.observableArray($(por.relatedproductoptionrows).map(function() { 
 
      return this.id; 
 
     }).get()); 
 
    }; 
 
    var ProductOptionModel = function (data) { 
 
     var self = this; 
 
     self.productoptions = ko.mapping.fromJS(data, mappingOptions); 
 
     self.isOpen = ko.observable(false); 
 
     self.selectedrelatedproductoptionrows = ko.observableArray([]); 
 
     /*Control Events*/ 
 
     self.addProductOption = function() { 
 
      var newoption = ko.mapping.fromJS({ 
 
       grouptitle: "Please select the number of years license", 
 
       productoptionrows: ko.observableArray([{ 
 
        id: "15", 
 
        producttitle: "25-100", 
 
        relatedproductoptionrows: [] 
 
       }, { 
 
        id: "16", 
 
        producttitle: "101-250", 
 
        relatedproductoptionrows: [] 
 
       }, { 
 
        id: "17", 
 
        producttitle: "251-500", 
 
        relatedproductoptionrows: [] 
 
       }]) 
 
      }, mappingOptions); 
 
      self.productoptions.push(newoption); 
 
     }; 
 
     self.copyProductOption = function (productoption) { 
 
      var copy = ko.mapping.fromJS(ko.mapping.toJS(productoption), mappingOptions); 
 
      self.productoptions.push(copy); 
 
     }; 
 
     self.removeProductOption = function (productoption) { 
 
      self.productoptions.remove(productoption); 
 
     }; 
 
     self.addProductOptionRow = function (productoption) { 
 
      var newrow = ko.mapping.fromJS({ 
 
       id: "15", 
 
       producttitle: "25-100", 
 
       relatedproductoptionrows: [] 
 
      }, mappingOptionsPR); 
 
      productoption.productoptionrows.push(newrow); 
 
     }; 
 
     self.removeProductOptionRow = function (productoption) { 
 
      $.each(self.productoptions(), function() { 
 
       this.productoptionrows.remove(productoption) 
 
      }) 
 
     }; 
 
     self.open = function (productoption, event) { 
 
      self.selectedrelatedproductoptionrows(productoption.relatedproductoptionrows); 
 
      self.isOpen(true); 
 
     }; 
 
     self.close = function() { 
 
      self.isOpen(false); 
 
     } 
 
    }; 
 
    ko.applyBindings(new ProductOptionModel(initialData), document.getElementById('page-wrapper')); 
 

 
});
<link href="https://code.jquery.com/ui/1.12.1/themes/ui-lightness/jquery-ui.css" rel="stylesheet" /> 
 
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script> 
 
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script> 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script> 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script> 
 
<script src="https://cdn.rawgit.com/gvas/knockout-jqueryui/075b303a/dist/knockout-jqueryui.min.js"></script> 
 

 
<div id="page-wrapper"> 
 
     <div> 
 
      <button title="Add Group Option" type="button" data-bind='click: $root.addProductOption'>Add Group Option</button> 
 
     </div> 
 
     <div id="options" data-bind="foreach: productoptions"> 
 
      <div style="padding:10px;margin:20px;background-color:whitesmoke"> 
 
       <table class="option-header" cellpadding="0" cellspacing="0"> 
 
        <thead> 
 
         <tr> 
 
          <th>Group Title <span class="required">*</span></th> 
 
          <th> 
 
           <button title="Copy" type="button" class="" style="" data-bind='click: $root.copyProductOption'>Copy Group</button> &nbsp;&nbsp; 
 
           <button title="Delete Option" type="button" data-bind='click: $root.removeProductOption'>Delete Group Option</button> 
 
          </th> 
 
         </tr> 
 
        </thead> 
 
        <tbody> 
 
         <tr style="height:36px;"> 
 
          <td> 
 
           <input type="text" data-bind='value: grouptitle'> 
 
          </td> 
 
          <td></td> 
 
         </tr> 
 
        </tbody> 
 
       </table> 
 
       <div> 
 
        <table class="option-header-rows" cellpadding="0" cellspacing="0"> 
 
         <thead> 
 
          <tr class="headings"> 
 
           <th>Id</th> 
 
           <th colspan="2" class="type-title">Product Title <span class="required">*</span></th> 
 
           <th>Related Ids</th> 
 
           <th></th> 
 
          </tr> 
 
         </thead> 
 
         <tbody data-bind="foreach: productoptionrows"> 
 
          <tr> 
 
           <td align="center"> 
 
            <input required type="text" style="width:40px" data-bind='value: id'> 
 
           </td> 
 
           <td colspan="2"> 
 
            <input type="text" value="25-100" data-bind='value: producttitle'> 
 
           </td> 
 
           <td> 
 
            <input type="text" data-bind='value: relatedproductoptionrowscsv' name="isdefault"><a href="#" data-bind="click: $root.open, disable: $root.isOpen">Lookup</a> 
 
           </td> 
 
           <td> 
 
            <button title="Delete Row" type="button" data-bind='click: $root.removeProductOptionRow'>Delete Row</button> 
 
           </td> 
 
          </tr> 
 
         </tbody> 
 
         <tfoot> 
 
          <tr> 
 
           <td align="right"> 
 
            <button title="Add New Row" type="button" data-bind='click: $root.addProductOptionRow'>Add New Row</button> 
 
           </td> 
 
          </tr> 
 
         </tfoot> 
 
        </table> 
 
       </div> 
 
      </div> 
 
     </div> 
 
     <!-- popup --> 
 
     <div data-bind="dialog: { isOpen: isOpen,title:'Select relations', modal:true }"> 
 
      <div data-bind="foreach: $root.productoptions"> 
 
       <div data-bind='text: grouptitle'></div> 
 
       <div data-bind="foreach: productoptionrows"> 
 
        <div> 
 
         <input type="checkbox" data-bind="value:id, checkedValue: selectedrelatedproductoptionrows" style="width:auto" /> 
 
         ID <span data-bind='text: id'></span> - <span data-bind='text: producttitle'></span> 
 
        </div> 
 
       </div> 
 
      </div> 
 
     </div> 
 
     <pre data-bind="text: ko.toJSON($data, null, 2)"></pre> 
 
    </div>

をこれは数日間stcukされました。事前に おかげ

答えて

1

免責事項:それはあなたがこの質問を投稿前の時間に答えるために時間を割いてから私を停止したものですので、私は

問題...、あなたのコードの「UI」の部分を削除します」再記述はかなり複雑になる可能性があります。キーはreadwriteオプションを持つko.computedのプロパティを使用することです。

したがって、ProductsOptionsという2つのリストがあります。各製品には1つ以上のオプションがあります。したがって、各オプションは0以上のリンクされた製品を持つことができます。 (これは多対多関係の意味です)

まず、プロットのリストをレンダリングします。各製品にはチェックボックス付きのオプションが表示されます。チェックされたオプションのリストを格納します。 HTMLで

function Product(data) { 
    this.title = data.producttitle; 
    this.id = data.id; 

    this.options = data.relatedproductoptionrows; 
    this.selectedOptions = ko.observableArray([]); 
}; 

<div data-bind="foreach: options"> 
    <label> 
    <input type="checkbox" 
      data-bind="checked: $parent.selectedOptions, checkedValue: $data"> 

    <span data-bind="text: producttitle"></span> 
    </label> 
</div> 

ます(UN)は、オプションのいずれかをチェックするたびに、オプションのオブジェクトはselectedOptions配列から追加または削除されます。

は、今最も困難な部分を開始します。私たちは代わりにProductOptionをレンダリングするとき、我々は製品が関連している(A)計算する必要があり、我々は、(B)これらの製品はselectedOptions配列はとどまることを確認する必要があります関係を変更することを選択した時点で最新の状態になります。(A)を皮切り

:我々はそうのようなオプションに関連する製品を定義することができます。

// Every product that has an option with my `id` is a related product 
relatedProducts = products.filter(
    p => p.options.some(o => o.id === this.id) 
); 

これらの関係のそれぞれからの読み取りまたは書き込みができ、計算checked状態を持っています。 ...私はかなり難しい概念のは把握して想像することができます

checked: ko.computed({ 
    // When the current `option` is in the linked product's 
    // selected options, it must be checked 
    read:() => p.selectedOptions().includes(linkedObj), 

    // When forcing the checked to true/false, 
    // we need to either add or remove the option to the 
    // linked product's selection 
    write: val => val 
    ? p.selectedOptions.push(linkedObj) 
    : p.selectedOptions.remove(linkedObj) 
}) 

(B)と私の説明は次のようになります。リード/ ko.computedが入って来書くところは、各関係(linkedObj)について、checked状態が定義されているのです欠けている。下の例では、この概念を実際に示しています。速度が最適化されていないこと(配列をループすることが多い)、チェックされたプロパティだけが観測可能であることに注意してください。

const products = getProducts(); 
 
const options = getOptions(); 
 
    
 
function Product(data) { 
 
    this.title = data.producttitle; 
 
    this.id = data.id; 
 
    
 
    this.options = data.relatedproductoptionrows; 
 
    this.selectedOptions = ko.observableArray([]); 
 
}; 
 

 
Product.fromData = data => new Product(data); 
 

 
function Option(data, products) { 
 
    this.title = data.producttitle; 
 
    this.id = data.id; 
 
    
 
    this.products = products 
 
    // Only include products that allow this option 
 
    .filter(
 
     p => p.options.some(o => o.id === this.id) 
 
    ) 
 
    // Create a computed checked property for each product- 
 
    // option relation 
 
    .map(p => { 
 
     // The `option` objects in our product are different 
 
     // from this instance. So we find our representation 
 
     // via our id first. 
 
     const linkedObj = p.options.find(o => o.id === this.id); 
 
     
 
     return { 
 
     checked: ko.computed({ 
 
      // Checked when this option is in the selectedOptions 
 
      read:() => p.selectedOptions().includes(linkedObj), 
 
      // When set to true, add our representation to the selection, 
 
      // when set to false, remove it. 
 
      write: val => val 
 
      ? p.selectedOptions.push(linkedObj) 
 
      : p.selectedOptions.remove(linkedObj) 
 
     }), 
 
     title: p.title 
 
     }; 
 
    }); 
 
} 
 

 
var App = function(products, options) { 
 
    this.products = products.map(Product.fromData); 
 
    this.options = options.map(o => new Option(o, this.products)); 
 
}; 
 

 
ko.applyBindings(new App(products, options)); 
 

 

 
// Test data 
 
function getProducts() { 
 
    return [{ 
 
    id: "1", 
 
    producttitle: "25-100", 
 
    relatedproductoptionrows: [{ 
 
     id: "4", 
 
     producttitle: '1 Year' 
 
    }, { 
 
     id: "5", 
 
     producttitle: '2 Year' 
 
    }, { 
 
     id: "6", 
 
     producttitle: '3 Year' 
 
    }] 
 
    }, { 
 
    id: "2", 
 
    producttitle: "101-250", 
 
    relatedproductoptionrows: [{ 
 
     id: "7", 
 
     producttitle: '1 Year' 
 
    }, { 
 
     id: "8", 
 
     producttitle: '2 Year' 
 
    }, { 
 
     id: "9", 
 
     producttitle: '3 Year' 
 
    }] 
 
    }, { 
 
    id: "3", 
 
    producttitle: "251-500", 
 
    relatedproductoptionrows: [{ 
 
     id: "10", 
 
     producttitle: '1 Year' 
 
    }, { 
 
     id: "11", 
 
     producttitle: '2 Year' 
 
    }, { 
 
     id: "12", 
 
     producttitle: '3 Year' 
 
    }] 
 
    }]; 
 
}; 
 

 
function getOptions() { 
 
    return [{ 
 
     id: "4", 
 
     producttitle: "1 Year", 
 
     relatedproductoptionrows: [] 
 
    }, { 
 
     id: "5", 
 
     producttitle: "2 Year", 
 
     relatedproductoptionrows: [] 
 
    }, { 
 
     id: "6", 
 
     producttitle: "3 Year", 
 
     relatedproductoptionrows: [] 
 
    }, { 
 
     id: "7", 
 
     producttitle: "1 Year", 
 
     relatedproductoptionrows: [] 
 
    }, { 
 
     id: "8", 
 
     producttitle: "2 Year", 
 
     relatedproductoptionrows: [] 
 
    }, { 
 
     id: "9", 
 
     producttitle: "3 Year", 
 
     relatedproductoptionrows: [] 
 
    }, { 
 
     id: "10", 
 
     producttitle: "1 Year", 
 
     relatedproductoptionrows: [] 
 
    }, { 
 
     id: "11", 
 
     producttitle: "2 Year", 
 
     relatedproductoptionrows: [] 
 
    }, { 
 
     id: "12", 
 
     producttitle: "3 Year", 
 
     relatedproductoptionrows: [] 
 
    }]; 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script> 
 
<div style="display: flex"> 
 
    <ul data-bind="foreach: products"> 
 
    <li> 
 
     <p data-bind="text: title"></p> 
 
     <div data-bind="foreach: options"> 
 
     <label> 
 
      <input type="checkbox" data-bind="checked: $parent.selectedOptions, checkedValue: $data"> 
 
      <span data-bind="text: producttitle"></span> 
 
     </label> 
 
     </div> 
 

 
    </li> 
 
    </ul> 
 

 
    <ul data-bind="foreach: options"> 
 
    <li> 
 
     <p data-bind="text: title"></p> 
 
     <div data-bind="foreach: products"> 
 
     <label> 
 
      <input type="checkbox" data-bind="checked: checked"> 
 
      <span data-bind="text: title"></span> 
 
     </label> 
 
     </div> 
 
    </li> 
 
    </ul> 
 
</div>

関連する問題