I have a Vue component inside a Vue loop on my website. Here's the JS file:
Vue.component('fb-question-text', {
props: ['question'],
template:
'<label>Prompt</label><input type="text" class="form-control" v-model="question.prompt"><a href="javascript:void" class="fb-remove">Remove</a>'
});
var questionList = new Vue({
el: '#questions',
data: {
questions: [
{
type: 'text',
id: 'question1',
prompt: ''
},
{
type: 'choice',
id: 'question2',
prompt: '',
choices: ['', '']
}
]
}
});
This is what my HTML file looks like:
<ul id="questions">
<li v-for="(question, index) in questions">
<h4>Question {{ index + 1 }}</h4>
<fb-question-text v-if="question.type === 'text'" :question="question"></fb-question-text>
</li>
</ul>
As you can see, I am trying to render the fb-question-text component if the question.type is of type "text". While the <li>
elements do render in the page, the component template does not render entirely. Only the first DOM element inside the template is rendered (in this case, the <label>
element). The input box and <a>
that are inside the component do not get rendered for some reason. When I remove the label, the input box gets rendered but nothing after it.
Can someone tell me what is going on?
Templates must have a single root element.
<span>
<label>Prompt</label>
<input type="text" class="form-control" v-model="question.prompt">
<a href="javascript:void" class="fb-remove">Remove</a>
</span>
Your fb-question-text
is only allowed to have one root element. You need to wrap that in a div.
Add <div>
around template
html
Vue.component('fb-question-text', { props: ['question'], template: '<div><label>Prompt</label><input type="text" class="form-control" v-model="question.prompt"><a href="javascript:void" class="fb-remove">Remove</a></div>' }); var questionList = new Vue({ el: '#questions', data: { questions: [ { type: 'text', id: 'question1', prompt: '' }, { type: 'choice', id: 'question2', prompt: '', choices: [ '' , '' ] } ] } });
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.11/vue.js"></script> <ul id="questions"> <li v-for="(question, index) in questions"> <h4>Question {{ index + 1 }}</h4> <fb-question-text v-if="question.type === 'text'" :question="question"></fb-question-text> </li> </ul>
For a similar question I purpose an other answer : Vue js error: Component template should contain exactly one root element
copy paste here :
if, for any reasons, you don't want to add a wrapper (in my first case it was for <tr/>
components), you can use a functionnal component.
Instead of having a single components/MyCompo.vue
you will have few files in a components/MyCompo
folder :
components/MyCompo/index.js
components/MyCompo/File.vue
components/MyCompo/Avatar.vue
With this structure, the way you call your component won't change.
components/MyCompo/index.js
file content :
import File from './File';
import Avatar from './Avatar';
const commonSort=(a,b)=>b-a;
export default {
functional: true,
name: 'MyCompo',
props: [ 'someProp', 'plopProp' ],
render(createElement, context) {
return [
createElement( File, { props: Object.assign({light: true, sort: commonSort},context.props) } ),
createElement( Avatar, { props: Object.assign({light: false, sort: commonSort},context.props) } )
];
}
};
And if you have some function or data used in both templates, passed them as properties and that's it !
I let you imagine building list of components and so much features with this pattern.
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.