-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
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
Allow more than 1 root element for Template #7088
Comments
Can you build a specific scenario in which you need a template with multiple root nodes? I've been using Bootstrap and tables with Vue since the beginning and have never needed multiple root nodes. |
FYI you can use You can have multiple root elements in functional components: render: h => [h('p', 'one'), h('p', 'two')] Personally, I actually like the fact that we only have one root as it clears out the questions you just listed because there's only one possible answer |
On my very specific Table use case I have a Category.vue <template>
<tbody>
<tr @click="showItems = true">
<td>{{ category.id }}</td>
<td>{{ category.name }}</td>
</tr>
</tbody>
<tbody v-show="showItems">
<tr v-for="item in category.items">...<tr>
</tbody>
</template> Now I know I could move them to two separate components and have them communicate, but they really belong together, they have computed properties and methods they will share, and yes I can move those to a mixin, but that just makes everything a bit more complex. Now, regarding Bootstrap, I recently stumbled on this: <div class="input-group">
<span class="input-group-addon"><i class="fa fa-dollar"></i></span>
<input v-show="isDisabled" class="form-control form-control-sm"/>
<vue-numeric v-show="!isDisabled" class="form-control"/>
</div> Now, the input/vue-numeric pair is reusable, I actually have quite some logic around those two, so I wanted to reuse them, but the input-group itself is not reusable, since in some places I don't need an input group or the input group is very different. I truly love Vue, this is really just neat picking. It would just be nice to have the option. The nice thing about this feature is that if you don't want to you don't have to use it even think about it, just keep wrapping everything in a div. |
I was wondering about that, I remember reading that long time ago, but couldn't find it, thanks for the tip |
The new react version has Fragment: https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html. They can do: <template>
<> <!-- render nothing -->
<tbody>
<tr @click="showItems = true">
<td>{{ category.id }}</td>
<td>{{ category.name }}</td>
</tr>
</tbody>
<tbody v-show="showItems">
<tr v-for="item in category.items">...<tr>
</tbody>
</>
</template> What about using this syntax in Vue ? |
Honest question here, why do we even need to wrap everything on a single root element, is this a technical limitation or an arbitrary decision? |
Technical, due to how the diff algorithm is written. It's obviously possible to update it, but it takes significant changes to the current algorithm (React did that during a complete rewrite). |
Got it, thank you for the details |
@sirlancelot One case is when you're using Nuxt, where everything is a Vue SFC. |
How's this very technical to implement? <template>
<tbody></tbody>
<tbody></tbody>
</template> can easily be transpiled to render: h => [ h('tbody'), h('tbody') ] thus solving the problem (idk what it was doing before, but this is easy to do). |
Here's proof that it works: https://jsfiddle.net/b049qboe/1 It doesn't seem like that requires any update to the diff algo. |
@trusktr The technical challenge is not with the conversion of template to render function, it's with the implementation of the virtualdom which the render function builds nodes for. Each child component is represented in its parent virtual dom by a single (Sidenote about your fiddle: functional components don't have that restriction because the are not represented with a vnode in the parent, since they don't have an instance and don't manage their own virtualdom) Allowing fragments requires significant changes to that algorithm, since we now would somehow have to keep the parent informed at all times about how many root nodes the child is currently managing in the real DOM, so when the parent re-renders, it knows how many HTML-Elements it has to "skip" to reach the next HTML Element that doesn't belong to the child component, That's a very intricate/complicated piece of code at the heart of Vue, and it is critical for render performance - so it's not only important to make it work correctly but also to make it highly performant That's a pretty hefty task. As Evan mentioned, React waited for a complete re-write of its rendering layer to remove that restriction. |
Are you implying that opting not to convert multiple roots from a template into multiple roots in a render function (which works) is because of performance, but it nonetheless would work? Can you make a fiddle that shows when it doesn't work? |
No. I'm saying that the current virtualDOM diff&patch algorithm heavily relies on the fact that each child component always has exactly one root element, so it would break completely with more than one root node in a child component. And I'm saying that making it work with more than one root component is more complicated, it adds additional logic, so it's a challenge to make this change without negatively impacting render performance in the current implementation. |
I'm still not convinced that multiple roots is even needed. If the core team is currently working on supporting it then that's great, but I don't feel like they should if it's not on the roadmap already. Every time I've thought "Hey I might need multiple root nodes for this," it puts me on a dangerous path of adding too much complexity to a single component. I always end up with a better, simpler solution that lives well within the single root node paradigm. Most of my solutions for the above usually rely on scoped slots. I highly recommend learning how to use them well. You will never need to think about multiple root nodes again. |
It's a "nice to have" on the roadmap, and won't happen anytime soon, as it would also be a breaking change - any 3rd-party component written with multiple root nodes wouldn't work with Otherwise, solid advice about complexity and scoped slots. |
Can one of you show how to rewrite my above fiddle with "scoped slots" to show that it is possible to output two When I tried using slots, it would'nt let me put a slot element as root of a component. @LinusBorg, will the ability to return an array from a |
No, why would you think we would remove anything? Returning an array from a render function doesn't work, never worked and will continue not to work in Vue 2 - the notable exception were, are and will be functional components, for the reasons l laid out further up. If you need help with a specific challenge in implementing a feature, forum.vuejs.org or chat.vuejs.org are the approriate place, not this issue. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@adi518 but Should it render any element? no it shouldn't <template>
<vue-fragment>
.... render as many component you want, fragment will no create any wrapper component.
</vue-fragment>
</template> |
This comment has been minimized.
This comment has been minimized.
It won't work in the sense that the Portal component adds its wrapper element, as it is a stateful component |
I'm sorry if i'm posting stupid answer. I'm still rookie in VueJS. I think I kinda found a suitable solution which supports all the requirements : fragment can be root, and are not functional. Can you guys check this : https://www.npmjs.com/package/vue-fragments ? |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Two solutions have existed for quite some time now:
@posva Can we get this thread locked so that future readers will see these solutions? |
Closing as this was implemented in Vue 3 but cannot be backported to Vue 2 |
What problem does this feature solve?
Right now you can only have 1 root element per template. I know this is by design, but I find myself wrapping everything around a
<div>
a lot. Now, most of the time is not a big deal, I can live with that, the problem is when either Bootstrap requires a very specific hierarchy, or when dealing with Tables that also require a very specific hierarchy and wrapping everything on a div is not an option.What does the proposed API look like?
Now, there will be a couple of things to figure out, mainly to what element will the properties provided on the Custom Element will be attached to. I think there two things that can be done:
The text was updated successfully, but these errors were encountered: