source

슬롯이 있는 동적 컴포넌트

gigabyte 2022. 8. 18. 23:46
반응형

슬롯이 있는 동적 컴포넌트

부모 컴포넌트의 동적 컴포넌트에서 명명된 슬롯을 사용하려면 어떻게 해야 합니까?

슬라이더 컴포넌트는 다음과 같은 동적 슬라이드 컴포넌트를 사용합니다.

<slider :slides="slides" />

각 슬라이드에는, 슬라이더로 사용하는 컨텐츠가 있는 슬롯이 있습니다.

<template>
<div class="slide">
  <div slot="main">Slide 1 Main</div>
  <div slot="meta">Slide 1 Meta</div>
</div>
</template>

슬라이더는 다음과 같은 슬롯을 사용합니다.

<template>
<div class="slider">
  <div class="slider__slide" v-for="slide in slides">
    <component :is="slide">
      <div class="slider__slide__main">
        <slot name="main" /><!-- show content from child's slot "main" -->
      </div>
      <div class="slider__slide__meta">
        <slot name="meta" /><!-- show content from child's slot "meta" -->
      </div>
    </component>
  </div>
</div>
</template>

그렇지만<component>는 내부 내용을 무시하기 때문에 슬롯은 무시됩니다.

예:
https://codepen.io/anon/pen/WZjENK?editors=1010

이것이 불가능할 경우 슬라이드 컴포넌트에서 HTML 콘텐츠를 가져오는 슬라이더를 만들 수 있는 다른 방법이 있습니까?

메인/메타 섹션을 자체 구성요소로 분할하면 렌더링 기능을 비교적 쉽게 사용하여 원하는 섹션으로 분할할 수 있습니다.

console.clear()

const slide1Meta = {
  template:`<div>Slide 1 Meta</div>` 
}
const slide1Main = {
  template: `<div>Slide 1 Main</div>`
}
const slide2Meta = {
  template:`<div>Slide 2 Meta</div>` 
}
const slide2Main = {
  template: `<div>Slide 2 Main</div>`
}

Vue.component('slider', {
  props: {
    slides: {
      type: Array,
      required: true
    }    
  },
  render(h){
    let children = this.slides.map(slide => {
      let main = h('div', {class: "slider__slide__main"}, [h(slide.main)])
      let meta = h('div', {class: "slider_slide_meta"}, [h(slide.meta)])
      return h('div', {class: "slider__slide"}, [main, meta])
    })
    return h('div', {class: "slider"}, children)
  }
});


new Vue({
  el: '#app',
  data: {
    slides: [
      {meta: slide1Meta, main: slide1Main}, 
      {meta: slide1Meta, main: slide2Main}
    ]
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
  <slider :slides="slides" />
</div>

<script type="text/x-template" id="slide1-template">
  <div class="slide">
    <div slot="main">Slide 1 Main</div>
    <div slot="meta">Slide 1 Meta</div>
  </div>
</script>

<script type="text/x-template" id="slide2-template">
  <div class="slide">
    <div slot="main">Slide 2 Main</div>
    <div slot="meta">Slide 2 Meta</div>
  </div>
</script>

실제로 다이내믹 내의 슬롯component요소가 작용합니다.저는 이 문제를 해결하기 위해 노력해왔고, 코드펜에서 패트릭 오더커의 사랑스러운 작은 예를 발견했습니다.패트릭은 그의 코드에 풍부하고 유용한 코멘트를 했고 나는 후세를 위해 여기에 말 그대로 붙인다.CodePen에서 찾을 수 있는 css를 생략했습니다.

const NoData = {
    template: `<div>
        This component ignores the data completely. 
        <p>But there are slots!</p>
        <slot></slot> 
        <slot name="namedSlot"></slot>
    </div>`
    // In this component, I just ignore the props completely
}
    
const DefaultMessage = {
    template: `<div>
        This component will show the default msg: <div>{{parentData.msg}}</div>
    </div>`,
    // this component won't have posts like the Async Component, so we just ignore it
    props: ['parentData']
}
    
const CustomMessage = {
    template: `<div>
        This component shows a custom msg: <div>{{parentData.msg}}</div>
    </div>`,
    // this component won't have posts like the Async Component, so we just ignore it
    props: ['parentData']
}
    
const Async = {
    template: `<div>
        <h2>Posts</h2>
        <p>{{parentData.msg}}</p>
        <section v-if="parentData.posts.length > 0">
            <ul>
                <li class="postInfo" v-for="post in parentData.posts">
                    <div class="postInfo__title">
                        <strong>Title:</strong> {{post.title}}
                    </div>
                </li>
            </ul>
        </section>
    </div>`,
    props: ['parentData']
}
    
/* Children should only affect parent properties via an EVENT (this.$emit) */
const ChangeMessage = {
    template: `<div>
        <p>Type here to change the message from the child component via an event.</p>
        <div><input type="text" v-model="message" @input="updateDateParentMessage" /></div>
    </div>`,
    data() {
        return {
            // initialize our message with the prop from the parent.
            message: this.parentData.msg ? this.parentData.msg : '' 
        }
    },
    props: ['parentData'],
    /* Need to watch parentData.msg if we want to continue
      to update this.message when the parent updates the msg */
    watch: {
        'parentData.msg': function (msg) {
            this.message = msg  
        }
    },
    methods: {
        updateDateParentMessage() {
            this.$emit('messageChanged', this.message)
        }
    }
};

const Home = {
    template: `<section>
        <div class="wrap">
            <div class="right">
                <p><strong>Change the current component's message from the Home (parent) component:</strong></p>
                <div><input type="text" v-model="dataForChild.msg" /></div>
                <p><strong>Important!</strong> We do not change these props from the child components. You must use events for this.</p>
            </div>
        </div>

        <div class="controls">
            <button @click="activateComponent('NoData')">No Data</button>
            <button @click="activateComponent('DefaultMessage')">DefaultMessage</button>
            <button @click="activateComponent('CustomMessage', {posts: [], msg: 'This is component two'})">CustomMessage</button>
            <button @click="getPosts">Async First</button>
            <button @click="activateComponent('ChangeMessage', {msg: 'This message will be changed'})">Change Msg from Child</button>
            <button @click="deactivateComponent">Clear</button>
        </div>

        <div class="wrap">
            <div class="right">
                <h2>Current Component - {{currentComponent ? currentComponent : 'None'}}</h2>
                <!-- ATTN: Uncomment the keep-alive component to see what happens 
                    when you change the message in ChangeMessage component and toggle
                    back and forth from another component. -->

                <!-- <keep-alive> -->
                <component 
                    :is="currentComponent" 
                    :parentData="dataForChild" 
                    v-on:messageChanged="updateMessage">
                    <div class="slotData">This is a default slot</div>
                    <div slot="namedSlot" class="namedSlot">This is a NAMED slot</div>
                    <div slot="namedSlot" class="namedSlot"><p>Here we pass in the message via a slot rather than as a prop:</p>{{dataForChild.msg}}</div>
                </component>
                <!-- </keep-alive> -->
            </div>
        </div>
    </section>`,

    data() {
        return {
            currentComponent: false,
            /* You don't NEED to put msg and posts here, but
                I prefer it. It helps me keep track of what info
                my dynamic components need. */
            dataForChild: {
                // All components:
                msg: '', 
                
                // Async Component only
                posts: [] 
            }
        }
    },
    methods: {
        /**
         * Set the current component and the data it requires 
         *
         * @param {string} component The name of the component
         * @param {object} data The data object that will be passed to the child component
         */
        activateComponent(component, data = { posts: [], msg: 'This is a default msg.'}) {
            this.dataForChild = data;
            this.currentComponent = component;
        },
        deactivateComponent() {
            this.dataForChild.msg = '';
            this.currentComponent = false;
        },
        /* Hold off on loading the component until some async data is retrieved */
        getPosts() {
            axios.get('https://codepen.io/patrickodacre/pen/WOEXOX.js').then( resp => {
                const posts = resp.data.slice(0, 10) // get first 10 posts only.
                // activate the component ONLY when we have our results
                this.activateComponent('Async', {posts, msg: `Here are your posts.`})
            })
        },
        /** 
         * Update the message from the child
         *
         * @listens event:messageChanged
         * @param {string} newMessage The new message from the child component
         */
        updateMessage(newMessage) {
            this.dataForChild.msg = newMessage
        }
    },
    // must wire up your child components here
    components: {
        NoData,
        CustomMessage,
        DefaultMessage,
        Async,
        ChangeMessage
    }
} 

const routes = [
    { path: '/', name: 'home', component: Home}
];
    
const router = new VueRouter({
    routes
});
    
const app = new Vue({
    router
}).$mount("#app")

html,

<div id="app">
    <h1>Vue.js Dynamic Components with Props, Events, Slots and Keep Alive</h1>
    <p>Each button loads a different component, dynamically.</p>
    <p>In the Home component, you may uncomment the 'keep-alive' component to see how things change with the 'ChangeMessage' component.</p>

    <nav class="mainNav"></nav>
    <!-- route outlet -->
    <!-- component matched by the route will render here -->
    <section class="mainBody">
        <router-view></router-view>
    </section>
</div>

언급URL : https://stackoverflow.com/questions/46465325/dynamic-components-with-slots

반응형