KnockoutJS 按子 属性 数组对 ObservableArray 条目进行分组

KnockoutJS Grouping ObservableArray entries by child Property array

一段时间以来,我一直在思考实现这一结果的最佳方式,并成功调整了我的显示模板和 JS,但它不可扩展。

我有一个 observableArray,ContentGrid[],数组中的每个项目都有多个对象,包括一个包含对象 Product & Docs 的对象 Taxonomies{}。 Product 和 Docs 都包含与其分类相匹配的项目数组,例如 Product 包含 [ProductA] 和 Docs:[Photos].

我希望能够按产品 A、产品 B、产品 C 等对结果进行分组,但是,我似乎无法找到使之成为可能的正确语法。

您可以使用遍历 ContentGrid 数组的 pureComputed 为每个唯一产品创建一个组。

下例显示:

  • 创建一个 Map 来保存组。它的 keys 指的是 product 并且它的 values 将是您网格的一部分(即部分 ContentGrids)
  • 使用 for ... of
  • 遍历网格的行
  • 使用 for ... of
  • 遍历行中的所有产品
  • 确保 Map
  • 中有一个空数组(= 内容网格)
  • 确保此行已记录在此产品的组中

为了更容易将组渲染到 UI,我将 Map 实例转换为视图模型数组。每组得到一个模型

  • title指产品
  • rows指的是网格中与该产品匹配的部分。

如您所见,此分组将复制具有多个产品的行。如果您想实施不同的分组策略,您可能需要更新内部循环。

const contentGrid = ko.observableArray([
  { id: "Grid row 1", taxonomies: { product: [{ id: "Product C" }] } },
  { id: "Grid row 2", taxonomies: { product: [{ id: "Product B" }] } },
  { id: "Grid row 3", taxonomies: { product: [{ id: "Product A" }] } },
  { id: "Grid row 4", taxonomies: { product: [{ id: "Product A" }, { id: "Product B" }] } },
  { id: "Grid row 5", taxonomies: { product: [{ id: "Product A" }, { id: "Product B" }, { id: "Product C" }] } }
]);

// A computed list of all rows
const rowsByProduct = ko.pureComputed(() => {
  const rowsByProductId = new Map();
  
  for (const contentRow of contentGrid()) {
    for (const product of contentRow.taxonomies.product) {
      if (!rowsByProductId.has(product.id)) {
        rowsByProductId.set(product.id, []);  
      }
      
      // Add the content row for this product
      rowsByProductId.get(product.id).push(contentRow);
    }
  }

  // Format our map's entries to viewmodels
  return Array
    .from(rowsByProductId.keys())
    .sort()
    .map(productId => ({
      title: productId,
      rows: rowsByProductId.get(productId)
    }));
});

ko.applyBindings({ contentGrid, rowsByProduct })
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<h2>By product</h2>
<ul data-bind="foreach: rowsByProduct">
  <li>
    <p data-bind="text: title"></p>
    <ul data-bind="foreach: rows">
      <li data-bind="text: id"></li>
    </ul>
  </li>
</ul>