【问题标题】:Grouping over an {#each} loop in Svelte在 Svelte 中对 {#each} 循环进行分组
【发布时间】:2020-05-24 14:41:32
【问题描述】:

我有一个非同质对象数组,每个对象都使用<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 中,单独的 A 和 B 组件内部不能有这样的部分 HTML 元素,因为 Svelte 不允许在未闭合元素周围使用条件:

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

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

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

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

【问题讨论】:

    标签: javascript svelte


    【解决方案1】:

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

    我们的想法是重新排列您的数据,以便以您想要的方式循环它。操作数据以适应布局总是比其他方式更容易。

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

    [
      {
        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

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

    类似下面的方法可以解决问题(请注意,细长的#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

    【讨论】:

    • 我已经准备好称这是一个可行的解决方案,尽管我认为我以前曾尝试过,但后来我想起了为什么我所做的没有奏效。新的组数组不绑定到原始数​​据,它必须。看到这个:svelte.dev/repl/46757f90b10546f292287770231b0c07?version=3.22.3
    • 绑定转换后的数据就像将其转换为 Svelte 反应式声明一样简单,将 let groups = things.reduce(...) 替换为 $: groups = things.reduce(...)。见svelte.dev/repl/de1b8912d4554855a7be13319d7ce7e6?version=3.22.3
    • 请注意,这适用于您选择应用的任何数据转换,imo 就是它的美妙之处 - 保持布局结构不变,但更改数据转换以满足您的需求,并使其具有反应性或不取决于您的绑定要求。
    • 太棒了。而且非常简单。谢谢!
    猜你喜欢
    • 2020-02-04
    • 1970-01-01
    • 2015-04-08
    • 2011-01-21
    • 2011-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多