Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to partially hydrate components with scoped slots #194

Open
ohuu opened this issue Sep 21, 2022 · 2 comments
Open

Unable to partially hydrate components with scoped slots #194

ohuu opened this issue Sep 21, 2022 · 2 comments
Labels
documentation Improvements or additions to documentation

Comments

@ohuu
Copy link

ohuu commented Sep 21, 2022

I've created a toy example to illustrate this issue. It's possible that I'm trying to use Iles incorrectly, apologies if that is the case.

Here is a simple component which exposes a scoped slot

<script lang="ts" setup>
const props = defineProps<{
  items: { data: string }[];
}>();
</script>

<template>
  <div v-for="item in items">
    <slot :item="item" />
  </div>
</template>

I use it within the index page like so:

<script lang="ts" setup>
const items = [{ data: "A" }, { data: "B" }];
</script>

<template>
  <my-component :items="items" client:only>
    <template #default="{ item }">
      <span>{{ item.data }}</span>
    </template>
  </my-component>
</template>

This results in the following error:

image

When I remove client:only the component works as expected.

@ElMassimo
Copy link
Owner

Hi Oliver!

Scoped slots are not currently supported (some frameworks like Preact don't have a similar concept).

Because of the nature of scoped slots in Vue, the outer component would also need to be shipped client side in order for this to work.

Something that could be potentially supported is to allow pre-rendering scoped slots, but making them static at build time.

If you could share your use case (non toy example), it will help me to understand whether this enhancement is worth it, or whether it's a matter of documenting this better.

@ElMassimo ElMassimo added the documentation Improvements or additions to documentation label Sep 21, 2022
@ohuu
Copy link
Author

ohuu commented Sep 22, 2022

Thanks for the quick reply @ElMassimo. So, is this something we could support for Vue only without it affecting Preact or other frameworks? Or is there some fundamental reason why scoped slots can't be dynamic?

Because of the nature of scoped slots in Vue, the outer component would also need to be shipped client side in order for this to work.

That makes sense, isn't this something we could make iles js do?

Something that could be potentially supported is to allow pre-rendering scoped slots, but making them static at build time.

Not entirely sure what you mean by this.

Here's my non-toy example. It's basically a very simple gallery component that I want to reuse in a couple places on the site and style them quite differently.

<script setup lang="ts">
import { computed, ref, watchEffect } from "vue";

const props = withDefaults(
  defineProps<{ 
    items: (ProductFragment | ProductSummaryFragment)[]; 
    itemsToShow: 1 | 2 | 3; 
  }>(), { itemsToShow: 1 }
);

const colWidth = computed(() => {
  switch (props.itemsToShow) {
    case 1:
      return "w-full";
    case 2:
      return "w-1/2";
    case 3:
      return "w-1/3";
  }
});
const page = ref(0);
const totalPages = computed(() => props.items.length - props.itemsToShow);

const slider = ref<HTMLDivElement>();

function slide(dPage: number) {
  if (page.value + dPage >= 0 && page.value + dPage <= totalPages.value) {
    page.value += dPage;
  }
}

watchEffect(() => {
  if (slider.value) {
    const slideWidth = Math.round(slider.value.clientWidth / props.itemsToShow);
    const x = page.value * slideWidth;
    slider.value.style.transform = `translateX(-${x}px)`;
  }
});
</script>

<template>
  <div class="relative overflow-x-hidden whitespace-nowrap w-full">
    <div ref="slider" class="transition-transform">
      <div v-for="item in items" class="inline-block align-top" :class="colWidth">
        <slot name="gallery-item" :item="item" />
      </div>
    </div>

    <slot name="buttons" :slide="slide">
      <div class="absolute -translate-y-1/2 top-1/2 w-full">
        <button @click="slide(-1)" class="btn btn-primary btn-circle box-shadow-none">◀</button>
        <button @click="slide(+1)" class="btn btn-primary btn-circle box-shadow-none">▶</button>
      </div>
    </slot>
  </div>
</template>

One place I'm using this component is in the index page to display featured products. Like this:

<script lang="ts" setup>
...

const props = defineProps<{ products: ProductSummaryFragment[] }>();

...
</script>

<template>
  ...

  <d-carousel :items="products" :items-to-show="3">
    <template #gallery-item="{ item }">
      <div class="bg-white whitespace-normal w-full h-full p-4">
        <a class="link link-primary link-hover text-lg" href="">
          {{ item.name }}
        </a>
        <img class="mx-auto my-4 h-32" :src="item.thumbnail?.url" />
        <div v-html="shortDesc(item)" class="font-light" />
        <a class="absolute bottom-0 link link-primary link-hover align-baseline w-full">
          MORE INFO >
        </a>
      </div>
    </template>

    <template #buttons="{ slide }">
      <div class="absolute top-1/2 -translate-y-6 w-full">
        <button class="btn btn-primary btn-circle btn-sm box-shadow-none float-left !px-3" @click="slide(-1)">

        </button>
        <button
          class="btn btn-primary btn-circle btn-sm box-shadow-none float-right !px-3"
          @click="slide(+1)"
        >

        </button>
      </div>
    </template>
  </d-carousel>

  ...
</template>

For the time being I can just create separate components but it would be totally awesome if we could make Iles support scoped slots. They are a pretty useful thing in Vue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

2 participants