Vue js error: Component template should contain exactly one root element
I don't know what the error is, so far I am testing through console log to check for changes after selecting a file (for uploading).
When I run $ npm run watch
, i get the following error:
"Webpack is watching the files…
95% emitting
ERROR Failed to compile with 1 errors
19:42:29error in ./resources/assets/js/components/File.vue
(Emitted value instead of an instance of Error) Vue template syntax error:
Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.
@ ./resources/assets/js/components/AvatarUpload.vue 5:2-181 @ ./resources/assets/js/app.js @ multi ./resources/assets/js/app.js ./resources/assets/sass/app.scss"
My File.vue is
<template>
<div class="form-group">
<label for="avatar" class="control-label">Avatar</label>
<input type="file" v-on:change="fileChange" id="avatar">
<div class="help-block">
Help block here updated 4 🍸 ...
</div>
</div>
<div class="col-md-6">
<input type="hidden" name="avatar_id">
<img class="avatar" title="Current avatar">
</div>
</template>
<script>
export default{
methods: {
fileChange(){
console.log('Test of file input change')
}
}
}
</script>
Any ideas on how to solve this? What is actually the error?
Note This answer only applies to version 2.x of Vue. Version 3 has lifted this restriction.
You have two root elements in your template.
<div class="form-group">
...
</div>
<div class="col-md-6">
...
</div>
And you need one.
<div>
<div class="form-group">
...
</div>
<div class="col-md-6">
...
</div>
</div>
Essentially in Vue you must have only one root element in your templates.
For a more complete answer: http://www.compulsivecoders.com/tech/vuejs-component-template-should-contain-exactly-one-root-element/
But basically:
- Currently, a VueJS template can contain only one root element (because of rendering issue)
- In cases you really need to have two root elements because HTML structure does not allow you to create a wrapping parent element, you can use vue-fragment.
To install it:
npm install vue-fragment
To use it:
import Fragment from 'vue-fragment';
Vue.use(Fragment.Plugin);
// or
import { Plugin } from 'vue-fragment';
Vue.use(Plugin);
Then, in your component:
<template>
<fragment>
<tr class="hola">
...
</tr>
<tr class="hello">
...
</tr>
</fragment>
</template>
You need to wrap all the html into one single element.
<template>
<div>
<div class="form-group">
<label for="avatar" class="control-label">Avatar</label>
<input type="file" v-on:change="fileChange" id="avatar">
<div class="help-block">
Help block here updated 4 🍸 ...
</div>
</div>
<div class="col-md-6">
<input type="hidden" name="avatar_id">
<img class="avatar" title="Current avatar">
</div>
</div>
</template>
<script>
export default{
methods: {
fileChange(){
console.log('Test of file input change')
}
}
}
</script>