简体   繁体   中英

Truly Nested Components in Vue.js?

Is it possible to create semantically nested elements with Vue.js?

Example: let's say I'm building an 'accordion' element. Accordions are made up of a 'heading' and a 'content' section. The content can be toggled in and out of view by clicking the header. The final html for an element like this would be something like this:

<div class="accordion">
    <div class="heading">
        My Accordion
    </div>
    <div class="content">
        Accordions are fun! Loren ipsum dolor sit amet.
        This can be extensive text, include pictures, etc.
    </div>
</div>

I would like to be able to create such elements in my html using syntax like this:

<accordion>
    <heading>My Accordion</heading>
    <content>
        Accordions are fun! Loren ipsum dolor sit amet.
        This can be extensive text, include pictures, etc.
    </content>
</accordion>

The 'heading' and 'content' elements are not generic, and should only exist within the context of the parent 'accordion' element, so I believe they should be declared within the parent component's definition.

I know that in order to capture the innerHTML content of an element, we must use a <slot> element, so I tried using the following templates:

<template id="heading">
    <div class="heading">
        <slot></slot>
    </div>
</template>

<template id="content">
    <div class="content">
        <slot></slot>
    </div>
</template>

<template id="accordion">
    <div class="accordion">
        <heading></heading>
        <content></content>
    </div>
</template>

<div id="app">

    <accordion>
        <heading>My Accordion</heading>
        <content>
           Accordions are fun. Lorem ipsum dolor sit amet.
           I could add a lot more text here, or other elements.
        </content>
     </accordion>

</div>

And the Vue javascript...:

Vue.component('accordion', {
    template: '#accordion',
    components: {
        heading: {
            template: '#heading'
        },
        content: [
            template: '#content'
        }
    }
});

Vue({
    el: '#app'
});

Unfortunately, it doesn't work. I've read the official documentation several times, and within the 'Components' section, when it talks about <slot> elements, it seems to indicate we should be able to do it - but I can't for the life of me work out how... The docs actually mention an element with a structure like this:

<app>
    <app-header></app-header>
    <app-footer></app-footer>
</app>

...but it doesn't give simple, concrete examples of how to do it.

The way the information is passed from parent to child element is confusing, and I have been unable to find any tutorials online that show how to setup a nested element like this. Any guidance would be greatly appreciated.

so <heading> and <content> should also be Vue components?

then it should look like this:

<div id="app">
  <h5>Accordion test</h5>
  <accordion>
    <heading slot="heading">Heading Text</heading>
    <content slot="content">Content Text</content>
  </accordion>

<template id="accordion">
  <div class="header">
    <slot name="heading"></slot>
  </div>
  <div class="content">
    <slot name="content"></slot>
  </div>
</template>

<template id="heading">
  <div class="template-heading">
    <slot></slot>
  </div>
</template>

<template id="content">
  <div class="template-content">
    <slot></slot>
  </div>
</template>
</div>

and the JS:

var Heading = Vue.extend({
  template: '#heading'
})
var Content = Vue.extend({
  template: '#content'
})
var Accordion = Vue.extend({
  template: '#accordion',
  components: {
    heading: Heading,
    content: Content
  }
})
Vue.component('heading', Heading)
Vue.component('content', Content)
Vue.component('accordion', Accordion)

var App = new Vue({
  el: '#app',
  data() {
    return {
        test: 'Test'
    }
  }
})

So:

  • The content of the <heading> element goes into the slot inside of <heading> 's template.
  • And the whole template of <heading> goes into the <slot name="heading"> inside the template of <accordion>

Working fiddle: https://jsfiddle.net/Linusborg/ud9a614o/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM