如何在父组件中使用动态组件的命名槽?
滑块组件采用动态幻灯片组件数组:
<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内容而不关心其内容?
答案 0 :(得分:2)
通过将main / meta部分拆分为自己的组件,您可以相对轻松地使用渲染功能将它们分割成您想要的部分。
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>
答案 1 :(得分:1)
实际上,动态component
元素中的广告位确实可以工作。我一直在尝试解决相同的问题,并在lovely little example的CodePen上找到了Patrick O'Dacre。帕特里克(Patrick)在他的代码中做了很多有用的评论,在此我逐字粘贴以便于后人。我省略了在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>`,
// 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>`,
// 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>