Vue 3 The template root requires exactly one element - javascript

In Greet.vue
<template>
<h2> Hello {{ name }} </h2>
</template>
<script>
export default {
name: "Greet",
props:['name']
};
</script>
In App.vue
<template>
<Greet name="bruce"/>
<Greet name="leo" />
<Greet name="diana" />
</template>
<script>
import Greet from './components/Greet.vue'
export default {
name: 'App',
components: {
Greet,
}
}
</script>
First I encountered this problem. Then I follow it by.
"vetur.validation.template": false,
"vetur.validation.script": false,
"vetur.validation.style": false,
Now there is no error. But now there is only one Hello displayed in the browser. I should expect 3.

This occurs when the vetur extension cannot determine the version of Vue as it cannot resolve package.json.
The docs at https://vuejs.github.io/vetur/ state that if Vetur cannot find package.json and determine the version of Vue, it assumes 2.5. This is what generates the wrong error. Vue3 can have more than one element.
It is expecting to find this at the project root - ie where you open your editor. Try opening you editor so that package.json sits exactly on the first level. You do not need to adjust Vetur settings.

App.vue
<template>
<div>
<Greet name="bruce"/>
<Greet name="leo" />
<Greet name="diana" />
<div>
</template>
<script>
import Greet from './components/Greet.vue'
export default {
name: 'App',
components: {
Greet,
}
}
</script>
The above will solve template root requires exactly one element, while these vetur configurations only disable some code checks.
"vetur.validation.template": false,
"vetur.validation.script": false,
"vetur.validation.style": false,

Related

In Vue 3, what is the correct type for a Vue component property?

Some of our components use other components as property.
A trivial example: <my-interface-component :popup="myPopup"/>
Where myPopup will be a component with a open method that allows to open this external popup component with a message.
In Vue 2 we used to set this property like this:
/**
* #prop {Vue} popup A root popup component to use
*/
popup: {
type: Vue
},
And we could give either a component definition or an existing component reference.
But in Vue 3 there is no more such Vue object. Should I just use Object or is there a more explicit way?
We use the CDN version of Vue 3 with Vanilla JS.
Many thanks
The right type of a component is ComponentOptions|ComponentOptions['setup'] which are simplified for readability as mentioned here:
import {ComponentOptions, PropType } from 'vue'
props:{
popup: {
type: Object as PropType<ComponentOptions|ComponentOptions['setup']>
},
}
However it's recommended to pass components/elements as slots not as props :
Child component :
<template>
<div>
<slot name="popup" />
</div>
</template>
In parent :
<template>
<div>
<template #popup>
<MyPopup />
</template>
</div>
</template>

My custom vue directive in a .vue file is not executing

I created a Vue custom directive for the first time. But the directive is not initialized. I tried a new project and the codepen too. I really don't know the problem!
This is my Vue component:
<template>
<div id="app">
<div my-test>Some text...</div>
</div>
</template>
<script>
export default {
name: "App",
directives: {
"my-test": function(el) {
el.style.backgroundColor = "red";
console.log("This is my first directive!");
}
}
};
</script>
The sandbox:
https://codesandbox.io/s/angry-khorana-uuhd0?file=/src/App.vue
Thanks
To ensure compliance with naming standards (have a - dash in the name) and to avoid conflicts, by default, all directives registered by Vue are automatically prefixed with v-.
So change the markup to <div v-my-test>...</div>. See it working.

Vue component not mounting or rendering and no error messages

I have the following 2 components
BrewTitle.vue
<template>
<h1>{{ title }}</h1>
</template>
<script>
export default {
data() {
return {
title: "Brew Title"
};
},
created() {
console.log("title created")
}
};
</script>
Snackbar.vue
<template>
<h1>{{ title }}</h1>
</template>
<script>
export default {
data() {
return {
title: "Brew Title"
};
},
created() {
console.log("snackbar created")
}
};
</script>
How they are added to the index.js file
import Vue from "vue";
import BrewTitle from "./components/BrewTitle";
import Snackbar from "./components/Snackbar";
Vue.component("brewtitle", BrewTitle);
Vue.component("snackbar", Snackbar);
const app = new Vue({
el: "#app"
});
In my html template I have the following snippet
<div id="app">
<brewtitle />
<snackbar />
</div>
<script src="main.js"></script>
The components are almost identical, but the snackbar is nowhere to be found on the html page or in the view browser extension. There are no problems with webpack and there is no message in the browser.
What am I doing wrong?
Browsers don't support self-closing tags like these:
<brewtitle />
<snackbar />
Try having explicit closing tags instead:
<brewtitle></brewtitle>
<snackbar></snackbar>
If you use a self-closing tag for a component then the browser will just treat it as an opening tag. An implicit closing tag will be created when the parent element closes. That'll work fine if there are no other siblings but it will go wrong when there are.
So taking your original code as an example:
<div id="app">
<brewtitle />
<snackbar />
</div>
The <brewtitle> won't count as closed until it reaches the closing </div>. So this is equivalent to:
<div id="app">
<brewtitle>
<snackbar></snackbar>
</brewtitle>
</div>
So <snackbar> will be treated as a child of <brewtitle>. As brewtitle doesn't have a slot the snackbar will just be discarded.
This only applies if the HTML is being parsed directly by the browser. For anything parsed by Vue itself, such as in your .vue files, this won't be a problem.
From the official Vue documentation, https://v2.vuejs.org/v2/style-guide/#Self-closing-components-strongly-recommended
Components with no content should be self-closing in single-file components, string templates, and JSX - but never in DOM templates.
...
Unfortunately, HTML doesn’t allow custom elements to be self-closing - only official “void” elements.

Cannot render component inside component

I have really basic Vue app (on Rails):
hello_vue.js:
import Vue from 'vue/dist/vue.esm'
import TurbolinksAdapter from 'vue-turbolinks'
Vue.use(TurbolinksAdapter)
import CollectionSet from '../collection_set.vue'
document.addEventListener('turbolinks:load', () => {
const app = new Vue({
el: '#app',
components: { CollectionSet }
})
})
collection_set.vue:
<script>
import Collectable from './collectable.vue'
export default {
components: { Collectable }
}
</script>
<template>
<p>test</p>
<collectable />
</template>
collectable.vue:
<script>
export default {
name: 'collectable'
}
</script>
<template>
<p>test 2</p>
</template>
my webpage:
<div id="app"><collection-set /></div>
With above example I don't see anything, but when I remove <collectable /> from collection_set.vue, I see test. I don't have any errors.
Why collectable is not being rendered?
Change the template code of collection_set.vue to
<template>
<div>
<p>test</p>
<collectable />
</div>
</template>
the reason for error is that Component template should contain exactly one root element
Here we were trying to craete two root elements p and collectable
Now that I wrapped it within a parent div container, it works just fine.
Please try and let me know if it helps.
One suggestion is that always check into console of browser devtools to check what could be the issue. In this case, the console gave the exact error, also the code compilation failed with same error.
In Vue, each component must have only ONE root element, meaning you need to have a tag like <p> or <div> and inside it all your template.

Can't register component locally in Vue.js. "[Vue warn]: Unknown custom element"

Have a problem with registering component locally in Vue.js app.
I'm trying to use MovieCard.vue inside it's parent MainView.vue.
Made everythig as in the Vue docs, but still getting this error:
[Vue warn]: Unknown custom element: <movie-card> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
found in
---> <MainView> at src\components\MainView.vue
<VApp>
<App> at src\App.vue
<Root>
Here goes the code for MovieCard.vue(child component)
<template>
<div>
<!-- <v-card class="movie-card" height="30vh" hover>
<v-card-media :src="'http://image.tmdb.org/t/p/w500' + movie-info.poster_path" height="100%">
<v-card class="movie-card__info-cover" width="100%" height="100%">
{{this.movie-info}}
</v-card>
</v-card-media>
</v-card> -->
</div>
</template>
<script>
export default {
name: "MovieCard",
props: ["movie-info"],
data() {
return {
visible: false
}
},
created() {
console.log("Info:", this["movie-info"])
}
}
</script>
<style>
</style>
And for MainView.vue(parent component):
<template>
<v-container class="main-view" column>
<v-btn color="pink" dark small absolute bottom left fab></v-btn>
<v-tabs centered grow color="pink" slot="extension" slider-color="yellow">
<v-tab class="main-view__option" #click="setCategory('popular')">
Popular
</v-tab>
<v-tab class="main-view__option" #click="setCategory('upcoming')">
Upcoming
</v-tab>
<v-tab class="main-view__option" #click="setCategory('topRated')">
Top Rated
</v-tab>
</v-tabs>
<v-container class="movie-cards__container" fluid grid-list-xl>
<v-layout row wrap>
<v-flex xs3 md2 class="" v-for="n in this.movies.movieLists.list" :key="n.id">
<movie-card :movie-info="n"></movie-card>
</v-flex>
<infinite-loading #infinite="infiniteHandler" spinner="spiral"></infinite-loading>
</v-layout>
</v-container>
</v-container>
</template>
<script>
import { mapState, mapActions, mapMutations } from 'vuex';
import InfiniteLoading from 'vue-infinite-loading';
import MovieCard from "./MovieCard"
console.log(MovieCard)
export default {
name: 'MainView',
components: {MovieCard},
data () {
return {
chosenCategory: 'popular'
}
},
computed: {
...mapState([
'movies'
])
},
methods: {
...mapActions([
"getNextPageByCategory",
'setCategory'
]),
async infiniteHandler($state) {
await this.getNextPageByCategory(this.chosenCategory);
$state.loaded();
console.log("yay");
},
...mapMutations([])
},
components: {
InfiniteLoading
}
}
</script>
Also, I've noticed the fact that if i put the same component into my App.vue root component, it works as intended(like any other subcomponents, which are registered locally inside App.vue(for example MainView.vue)).
In addition, I'll say that i'm using Vuex and Vuetify inside my app.
Tried to solve this issue for hours, but for now no results...
Thank you in advance for your help!
try to define your component in MainView like this :
components: {
'movie-card': MovieCard
}
edit: maybe it's because you define your components twice
In the OP question, he made the mistake defining components twice in MainView.vue, so that the latter components overrode the first one, which is the one declared movie-card component, so only
components: {
InfiniteLoading
}
is the valid registered component at the end of the day,
components: {MovieCard},
was overridden
And today, I've gone through the same shit
Unknown custom element: <movie-card> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
And the mistake of mine was I mistyped the word components to component without the s. So if any of you guys go later and about to see this kind of error, chances you've mistyped something.
The Vue error prompt was also smart enough to ask us did you register the component correctly?, yet we made the assumption that we did, but hold on, we likely didn't.
That being said, the problem had nothing to do with
components: {
'movie-card': MovieCard
}
// the above is the same at the below in Vue
components: {
MovieCard
}
If you read Sovalina's answer, please notice that the answer is his edit
Another thing btw, I've thought the problem had something to do with recursive components, so I made sure providing the "name" option, but as you could read this, it didn't have a thing to do with "name" option cause movie-card was not a recursive component. In my case, I resolved my problem by adding a character s, not any name.

Categories