New directive: v-skip
#714
Replies: 10 comments 8 replies
-
I like this. It looks similar to https://developer.mozilla.org/zh-CN/docs/Web/CSS/display#display_contents |
Beta Was this translation helpful? Give feedback.
-
Useful directive reduce boilerplate code. The name |
Beta Was this translation helpful? Give feedback.
-
I agree this feels like a nice way to clean up what is otherwise a bit convoluted to get right in userland. I'd suggest that the name If you consider the template with and without this, if you don't use this directive, you get the parent element and its children rendered: <!-- rendered HTML without directive -->
<div>
<p>Hello</p>
</div> And when you do use this directive, you get only the children rendered. <!-- rendered HTML with this directive "activated" -->
<p>Hello</p> For this reason, the real function this directive performs is "unwrapping" the children by removing the parent. Hence why I think
|
Beta Was this translation helpful? Give feedback.
-
I think negatives should not be used as it increases cognitive load. v-if = true
v-show = true
// display the element when true.
v-wrap = true
// displays the wrapping element when true. having a true value remove an element is unintuitive, even if the naming is straight forward. |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
This original example whereby this template: <Tooltip v-skip="!disabled">
<button :disabled="disabled">Submit</button>
<template #content>
<em>The button is disabled because ...</em>
</template>
</Tooltip> ...renders this HTML: <button :disabled="disabled">Submit</button> seems a bit odd to me. It’s rendering the default slot content, but then other slots ( |
Beta Was this translation helpful? Give feedback.
-
This is a very rare case and 'userland' component does the job quite well |
Beta Was this translation helpful? Give feedback.
-
My stack overflow question is what triggered that blog post and there was no solution. This is a great idea and v-skip makes the most sense to me since you are essentially skipping a node in a tree and not unwrapping it. |
Beta Was this translation helpful? Give feedback.
-
Edge Cases to Consider for v-contentsLet’s assume it’s called v-contents. 1. Scoped Slots HandlingBasic Usage Scenario<Table v-contents="foo">
<template #row="{ item }">
<td>{{ item.name }}</td>
<td>{{ item.age }}</td>
</template>
</Table>
Nested Scoped Slots<DataProvider v-contents="foo">
<template #default="{ data }">
<List>
<template #item="{ item }">
{{ data.title }} - {{ item.name }}
</template>
</List>
</template>
</DataProvider>
Dynamic Slots<Component v-contents="foo">
<template #[dynamicSlot]="slotProps">
{{ slotProps.data }}
</template>
</Component> To address these issues, we need to:
This is a technical detail that requires special attention and may influence the final design of the directive. 2. Refs Handling<div v-contents="foo" ref="wrapper">
<span ref="content">content</span>
</div>
3. DOM Properties and Styles<div v-contents="foo" class="wrapper" :style="styles">
<span>content</span>
</div>
4. Combination with Other Directives<div v-contents="foo" v-show="bar">
<span>content</span>
</div>
5. Work with built-in componentsTeleport<teleport to="body" v-contents="foo">
<div>teleported content</div>
</teleport> Suspense<Suspense v-contents="foo">
<AsyncComponent />
</Suspense> KeepAlive<KeepAlive v-contents="foo">
<component :is="currentComponent" />
</KeepAlive> All these edge cases need careful consideration during implementation and should be clearly documented in the RFC. Some cases might require special handling or explicit behavior definitions. |
Beta Was this translation helpful? Give feedback.
-
Summary
This RFC proposes the addition of a new directive,
v-skip
. Thev-skip
directive conditionally skips rendering the current element or component while preserving and promoting its child nodes to the parent level in the node hierarchy. This behavior differs fromv-if
, which entirely removes the node and its subtree when the condition isfalse
.Basic example
Motivation
There are scenarios where developers need to conditionally remove an element or component without discarding its children. Currently, achieving this requires workarounds such as using userland components or switching to render functions, which can be verbose and less intuitive.
Related:
Allow for <component> to be a conditional wrapper ie. only render children. vue#12033
This issue discussed the need of a conditional wrapper. The workaround (bare basic, not robust) provided in one comments is:
There was also a comment proposing a
v-wrap-if
directive:Fragment component #449
This RFC propsed a built-in
fragment
component to make utilizing conditional wrappers easier.https://dev.to/alexander-nenashev/creating-a-conditional-wrap-component-in-vue-3-18ml
This post series introduced a way to implement a conditional wrapper component which is quite cumbersome and relied on some knowledge of Vue's internals. The usage of the component is also not as concise as a directive.
The userland component is quite hard to be implemented correctly and when it comes to toggling a component and passing through its props, slots and event listeners, it becomes verbose to use as well. It becomes even harder if we want correct type support.
The
v-skip
directive aims to provide a more concise and declarative approach to tackle this problem. It eliminates the need for userland components, eases the pain of passing propsor switching to render functions, and makes the code more readable and maintainable.
Detailed design
The
v-skip
directive accepts an expression that evaluates to a boolean value.For elements
If
condition
is truthy, the current node is skipped, and its children are rendered in its place (render as if it's a fragment node) and produces:Otherwise, the node and its children are rendered normally and produces:
For components
Let's say we have a
Tooltip
component which is used like this:Which renders:
We can conditionally skip the
Tooltip
component like this:disabled
is falsy (v-skip
evaluates totrue
), theTooltip
component is skipped, we directly render itsdefault
slot in its place and discard other slots, which produces:Implementation details
v-skip
directive should be rerendered when the condition changes as the their parent may have provided contextual data (provide
, etc.).v-skip
cannot be used on<template>
and<slot>
.Drawbacks
Alternatives
Alternatives in userland is already covered in the motivation section.
Regarding to the naming, we can also consider alternatives like:
v-skip-if
v-wrap
v-wrap-if
v-unwrap
v-unwrap-if
Adoption strategy
Backward Compatibility
The addition of
v-skip
does not break existing code and is entirely opt-in.Documentation
Comprehensive documentation and examples will be provided to educate developers on proper usage.
Tooling Support
We need built-in support from Vue Devtools and the official language support to recognize and correctly parse the
v-skip
directive. We also need to add linting rules toeslint-plugin-vue
enforce proper usage of the directive.Unresolved questions
N/A
Additional suggestions from comments
v-contents
Beta Was this translation helpful? Give feedback.
All reactions