在 Svelte 中对 {#each} 循环进行分组

Grouping over an {#each} loop in Svelte

我有一组非同类对象,每个对象都使用 <svelte:component this={type}> 组件在循环中呈现,我想将相同类型的相邻对象分组在 div.

例如,我有一些类似这样的代码:

<script>
let things = [
  {type: A, content: "One"},
  {type: B, content: "Two"},
  {type: B, content: "Three"},
  {type: A, content: "Four"}
];
</script>

{#each things as thing, i}
  {()=>someMagicHere1(things, thing, i)}
  <svelte:component this={thing.type}>
  {()=>someMagicHere2(things, thing, i)}
{/each}

我希望输出像这样分组:

<div class="group type-A">
  <div class="thing type-A">One</div>
</div>
<div class="group type-B">
  <div class="thing type-B">Two</div>
  <div class="thing type-B">Three</div>
</div>
<div class="group type-A">
  <div class="thing type-A">Four</div>
</div>

things数组中,事物没有排序(实际上,它们是排序的,但按日期,与它们的类型无关),但想法是在视觉上将相同类型的事物组合在一起.理想情况下,我只能对某些类型进行分组(比如将所有类型 A 分组,但类型 B 将保持独立),但我觉得如果我可以分组,我将能够得出一个单独的解决方案。也有两种以上;这只是一个最小的示例。

在 Svelte 中,individual A 和 B 组件不能有像这样的部分 HTML 元素,因为 Svelte 不允许围绕未闭合元素的条件:

{#if groupStart}
<div class="group">
{/if}

在每个 someMagicHereX() 中,我可以用 {@html customTags} 输出一些 HTML 以获得我想要的 DOM 输出,但是我失去了样式封装和其他Svelte 组件的好处。

我真正想要的是更 "sveltian" 的解决方案。也许我需要用 use 创造一些新东西?大家有什么好主意吗?

更新: 我似乎遗漏的一个关键特性是任何控件最终都必须绑定到原始数​​据集。因此,即使数据以某种方式进行了转换,原始数据也必须在运行时更新,反之亦然。

不确定以下内容是否满足您的要求,因为我无法从您的问题中判断您是想保持值的顺序不变,还是想先按组重新排序。

我们的想法是重新排列您的数据,以便按照您想要的方式遍历它。为了适应布局而操纵数据总是比反过来更容易。

以下内容会将您的初始数组转换为具有以下结构的数组:

[
  {
    cssClass: 'type-A',
    values: [ /* all objects of type 'A' */ ],
  },
  {
    cssClass: 'type-B',
    values: [ /* all objects of type 'B' */ ],
  },
  // etc.
]

数据转换非常简单:

let groups = things.reduce((curr, val) => {
  let group = curr.find(g => g.cssClass === `type-${val.type}`)
  if (group)
    group.values.push(val)
  } else {
    curr.push({ cssClass: `type-${val.type}`, values: [ val ] }) 
  }
  return curr
}, [])

有了这个新的数据结构,很容易实现您想要的布局:

{#each groups as group}
  <div class="group {group.cssClass}">
    {#each group.values as value}
      <div class="thing {group.cssClass}">
        {value.content}
      </div>
    {/each}
  </div>
{/each}

Demo REPL

编辑:如果您优先考虑对象在初始数组中的顺序,则数据转换会略有不同。基本上,每次类型更改时,您都希望创建一个新的 'group'。

像下面这样的东西就可以了(请注意,苗条的 #each 结构将保持不变,只有数据转换发生变化):

let groups = things.reduce((curr, val) => {
  let group = curr.length ? curr[curr.length - 1] : undefined 
  if (group && group.cssClass === `type-${val.type}`) {
    group.values.push(val)
  } else {
    curr.push({ cssClass: `type-${val.type}`, values: [ val ] }) 
  }
  return curr
}, [])

Option 2 Demo REPL