Skip to content

Commit

Permalink
[Vue] Use fragments by default with sc-placeholder (#742)
Browse files Browse the repository at this point in the history
* Use Vue fragment

* Update doc

* Update doc according the review
  • Loading branch information
illiakovalenko authored Jul 9, 2021
1 parent fa88779 commit 9582a7a
Show file tree
Hide file tree
Showing 3 changed files with 420 additions and 536 deletions.
56 changes: 5 additions & 51 deletions docs/data/routes/docs/client-frameworks/vue/vue-placeholders/en.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ export default{
</script>
```

The `name` is the key of the placeholder you're exposing, and the `rendering` is the current Sitecore-provided route data, or parent component data if you're exposing a placeholder from within another component.
The `name` is the key of the placeholder you are exposing, and the `rendering` is the current Sitecore-provided route data, or parent component data if you are exposing a placeholder from within another component.

The `Placeholder` component does not render a wrapper around child components. This is because of Vue's [fragment technique](https://v3.vuejs.org/guide/migration/fragments.html#overview).

## SitecoreJssPlaceholderPlugin technique

Expand Down Expand Up @@ -92,7 +94,7 @@ export default {

The `SitecoreJssPlaceholderPlugin` uses a mixin that attaches to the `beforeCreate` hook to look for a `withPlaceholder` property on a component definition. The plugin then uses the value provided by the `withPlaceholder` property to find the specified placeholder data, e.g. `tabs` in the `rendering` prop data. The plugin then creates a computed property on the component, using the name of the placeholder as the property name by default, and assigns an array of all the Vue components for that placeholder to the computed property. This allows you to use the built-in Vue [dynamic component](https://v3.vuejs.org/guide/component-basics.html#dynamic-components) to render the placeholder components in your template.

When you iterate the component array in your template, Vue will render the components where you emit them. The main advantage of this technique is that _there is no wrapper component_. If you use the `Placeholder` component, all child components render underneath it in the Vue component tree. If you emit the placeholder contents with this technique, the placeholder contents will have no wrapping component and will render inline. This is very useful when you're using Vue libraries that are based on a specific component hierarchy, for example this example of `vue-carousel`:
When you iterate over the component array in your template, Vue will render the components where you emit them, or you can achive this using `Placeholder` component. If you emit the placeholder contents with this technique, the placeholder contents will have no wrapping component and will render inline. This is very useful when you are using Vue libraries that are based on a specific component hierarchy, for example this example of `vue-carousel`:

```
<carousel>
Expand All @@ -102,62 +104,14 @@ When you iterate the component array in your template, Vue will render the compo
</carousel>
```

In the preceding sample it's expected that the component hierarchy is `Carousel` -> `Slide`. If you wished to add the `slide` components using a placeholder, so that Sitecore could define them, and this were done using the `Placeholder` component, the hierarchy would instead look like `Carousel` -> `Placeholder` -> `SitecoreSlideWrapper` -> `Slide`:
In the preceding sample it is expected that the component hierarchy is `Carousel` -> `Slide`. If you wished to add the `slide` components using a placeholder, so that Sitecore could define them, and this were done using the `Placeholder` component, the hierarchy will be the same.

```
<carousel>
<sc-placeholder name="jss-slides" :rendering="rendering" />
</carousel>
```

With the placeholder computed property, we can solve this in two different ways:

### Inline components

If the library doesn't mind a single layer of component wrapping, you can place the child component into your rendering component. This will result in a component hierarchy like `Carousel` -> `SitecoreSlideContainer` -> `Slide` in the sample below:

##### Carousel Container

```
<template>
<carousel>
<component v-for="(slide, index) in $options.computed.slidesPlaceholder" :is="slide" :key="`slide${index}`" />
</carousel>
</template>
<script>
export default {
name: 'ContainerComponent',
props: {
rendering: {
type: Object,
},
},
withPlaceholder: {
// you can alias the computed prop name for the placeholder or pass an array of placeholders
placeholders: {
placeholder: 'slides',
computedPropName: 'slidesPlaceholder',
},
},
};
</script>
```

##### Slide Container

```
<!-- This component would be added to the Sitecore placeholder and wraps the carousel `<slide />` component. -->
<template>
<slide>Your JSS or other Vue components here</slide>
</template>
<script>
export default {
name: 'SitecoreSlideContainer',
}
</script>
```

### Component transformation

If you need a completely flat component hierarchy (`Carousel` -> `Slide` in our example), you can take advantage of the computed prop being an array to transform the child components with a wrapper using an inline template. When using this technique, the child Sitecore components can be completely unaware of the wrapping and render only their content for a clean separation of concerns.
Expand Down
2 changes: 1 addition & 1 deletion packages/sitecore-jss-vue/src/components/Placeholder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,6 @@ export const Placeholder = defineComponent({
return () => scopedSlots.default({ components, isEmpty });
}
// Otherwise, if no default scoped slot is defined, assume "normal" rendering of the VNodes that were created from rendering data.
return () => (vnodes && vnodes.length > 0 ? h('div', {}, vnodes) : null);
return () => (vnodes && vnodes.length > 0 ? vnodes : null);
},
});
Loading

0 comments on commit 9582a7a

Please sign in to comment.