vuejs / vue

This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/core
http://v2.vuejs.org
MIT License
207.98k stars 33.69k forks source link

Allow more than 1 root element for Template #7088

Closed tochoromero closed 3 years ago

tochoromero commented 6 years ago

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: 1) Find the first child and stick everything to it. 2) Provide a custom attribute to indicate to what element they should be attached to.

y-nk commented 6 years ago

@adi518 added abstract so it's now totally transparent within the tree (parent and children are linked correctly without the v-fragment node in between)

anyone to review/correct/contribute-to-ssr ?

rentalhost commented 6 years ago

Additional note: while <tbody> will solves 90% of <table> issues, cases where it should generates two <option> need a bit of hack.

Simplified case:

<template>
    <option :value="x">X</option>
    <option :value="x + 1">X + 1</option>
</template>

Currently is impossible do that without some kind of wrapper hack, because anything inside <select> that not <optgroup> or <option> will be ignored by browser, and <optgroup> will change the behavior.

The same could be applied to example below:

<template v-for="x in range">
    <option :value="x" v-text="someMagic(x)"></option>
</template>

Someone have some clue to where we could try improve the current code to support that? At least a start point, so some devs could try to help the vue team too.

effulgentsia commented 6 years ago

@rentalhost: I'm not a Vue dev, but thank you for the <option> use-case. I'm curious, can you elaborate a bit more on it? What's your use-case of wanting <option> elements output in a different template than the one that outputs the <select>?

rentalhost commented 6 years ago

@effulgentsia so... basically, I have an API that will returns a file tree like that pastebin. User can select any file from some folder. So the final structure should be like (jsfiddle): (Note the InnerFolder indent because it is inside of OuterFolder.)

<select>
    <option disabled>🖿 OuterFolder</option>
    <option> FileA.ext</option>
    <option> FileB.ext</option>
    <option disabled></option>
    <option disabled> 🖿 InnerFolder</option>
    <option>  FileA.ext</option>
    <option>  FileB.ext</option>
</select>

The problem is that due to original API structure, the best way to do should be like:

<!-- VFolder.vue -->
<template>
    <select>
        <v-folder-option :folder="folder" :depth="0"></v-folder-option>
        <!-- where "folder" is the JSON root. -->
    </select>
</template>

<!-- VFolderOption.vue -->
<template>
    <option disabled></option>
    <option disabled v-text="depthSpacing() + folder.title"></option>
    <option v-for="node in folder.nodes" v-text="depthSpacing() + node.title"></option>
    <v-folder-option v-for="child in folder.children" :folder="child" :depth="depth + 1"></v-folder-option>
</template>

But this approach is impossible for now.

So what I had to did was to "collapse" the API JSON to a "single level", so I got something like that pastebin. With that I could build a yet compatible way, like that:

<!-- VFolder.vue -->
<template>
    <select>
        <template v-for="node in nodes">
            <template v-if="node.type === 'folder'">
                <option disabled></option>
                <option disabled v-text="depthSpacing(node.depth) + node.title"></option>
            </template>
            <template v-else>
                <option v-text="depthSpacing(node.depth) + node.title"></option>
            </template>
        </template>
    </select>
</template>

The vue seems more simple, but the collapse method should be considered as part of a new complexity.

sirlancelot commented 6 years ago

Two solutions have existed for quite some time now:

@posva Can we get this thread locked so that future readers will see these solutions?

posva commented 3 years ago

Closing as this was implemented in Vue 3 but cannot be backported to Vue 2