Vue: can't use a component inside another compontent - vue.js

I am trying to use a child compontent in another compontent and it does not work. I have been trying to solve this problem looking for typos etc. for hours, but can't find anything.
Menu.vue
<template>
<div class='navbar-and-alert'>
<alert/>
<nav class='navbar'>
</nav>
</div>
</template>
<script>
import Alert from './Alert.vue'
export default {
name: 'Navbar',
compontents: {
Alert
},
data (){
return {
}
},
}
</script>
Alert.vue
<template>
<section class='alert-section'>
<p class='alert-section__content'>
...
</p>
<a href=''><img src='/static/assets/img/close.svg' class='alert-section__close-icon'></a>
</section>
</template>
<script>
export default {
name: 'Alert',
}
</script>
I get this alert in console:
Vue warn]: Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option.
found in
The alert component works when used inside App.vue

components has a typo:
compontents: {
Alert
},
Should be:
components: {
Alert
},

Related

Vue: Reuse loading spinner template and logic

I've multiple (20+) pages where I need the following code:
<template>
<template v-if="isLoading">
<Spinner full size="medium" />
</template>
<template v-else>
<p>Page data</p>
</template>
</template>
<script>
export default {
computed: {
isLoading() {
return this.$store.getters['loader/isLoading'];
},
},
};
</script>
I don't want to rewrite this part everytime I need it so is there a way to create something like a higher order component with access to the computed method and a way to add the hoc to the script tag of the different vue files? Or another way to archive this?
I could recommend extending the spinner component where you want to show the spinner. I've created a boilerplate setup that show a very simple implementation of this approach here.
The main idea is to expose a default slot for you spinner component, and wrap the page component in that slot.
<template>
<div>
<Spinner v-if="isLoading" full size="medium" />
<!-- Slot for component data -->
<slot v-else></slot>
</div>
</template>
<script>
export default {
computed: {
isLoading() {
return this.$store.getters['loader/isLoading'];
},
},
};
</script>
Then in any component that you want to show the spinner:
<template>
<spinner>
<!-- Pass here the component content to be shown after the spinner is hidden -->
</spinner>
</template>
<script>
import Spinner from "./spinner";
export default {
name: "Page1",
extends: Spinner,
components: { Spinner }
};
</script>

vue can't querySelector for child component DOM element

I cannot search for child component DOM element, my settings are as follows:
pages/Login.vue
<template>
<section class="login">
<div v-show="step === 4" class="login__container">
<Test />
</div>
</section>
</template>
<script>
export default {
data () {
return {
step: 1
}
},
async mounted () {
this.step = 4
await this.$nextTick()
document.querySelector('.test') // NULL
},
}
</script>
components/Test.vue
<template>
<div class="test">
foo
</div>
</template>
setTimeout of course is not solution. I also try the same on other page, but without success. What am I doing wrong? I guess the problem must be somewhere in the template or project configuration
#edit
i tried to do the same effect on jsfiddle vue template and fresh nuxt project but no problem there
You could try to use ref instead of querySelector to manipulate the component DOM :
<template>
<section class="login">
<div v-show="step === 4" class="login__container">
<Test ref="test"/>
</div>
</section>
</template>
<script>
export default {
data () {
return {
step: 1
}
},
mounted () {
this.step = 4
let test=this.$refs.test
},
}
</script>
Another way to access child component is emitting event when its ready and created in DOM,
In the child element:
<template>
<div ref="test">foo</div>
</template>
<script>
export default {
mounted() {
this.$emit('childMounted', this.$refs.test)
}
}
...
In your parent:
<template>
<section class="login">
<div v-show="step === 4" class="login__container">
<Test #childMounted="childMounted"/>
</div>
</section>
</template>
<script>
export default {
data () {
return {
step: 1
}
},
methods: {
childMounted(childRef) {
// Try here
// childRef: your child component reference
}
}
}
</script>
This kind of code should work properly
parent.vue
<template>
<div>
<test ref="parentTest" #hook:mounted="selectChildElement"></test>
</div>
</template>
<script>
export default {
methods: {
selectChildElement() {
console.log(this.$refs.parentTest.$refs.test)
},
},
}
</script>
Test.vue component
<template>
<div ref="test">foo</div>
</template>
This is because of the way the parent and children components are mounted, as explained here: https://stackoverflow.com/a/44319825/8816585
As Brahim said, it is also better to use $refs in an SPA context, more info available here.
The #hook:mounted trick was taken from this answer and initially found in this dev.to post.
As I thought, the problem is with nuxt, namely auto-importing components.
I am using automatic component import in the nuxt configuration.
nuxt.config.js
components: [
{
path: '~/components',
pathPrefix: false,
},
],
This approach apparently breaks something, and only after manually importing the component did it work properly
import Test from '#/components/Test.vue'
export default {
name: 'LoginPage',
components: {
Test
},
So the nuxt configuration caused the problem. Thank you for all your help.

vm.$on listener isn't firing from the parent mounted() method

It seems very simple but not working for me. I am trying to fire event from a child component and listen to it from the parent component using the mounted() method by using the vm.$on() instance method but it's not working.
For example, I have created a very basic Vue CLI App on CodeSandbox to reproduce the issue. Any help would be much appreciated.
Parent component: App.vue
<template>
<div id="app">
<HelloWorld />
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
name: "App",
components: {
HelloWorld
},
mounted() {
this.$on('icecream', () => console.log('not good for children'));
},
};
</script>
Child component: HelloWorld.vue
<template>
<div class="hello">
<h3>Emit event from child and listen from parent</h3>
<button #click="emitAnEvent()">Emit</button>
</div>
</template>
<script>
export default {
name: "HelloWorld",
methods: {
emitAnEvent() {
this.$emit('icecream');
}
},
};
</script>
There's also another option to handle the $emit() written in the Vue Documentation here.
<HelloWorld v-on:icecream="CatchIceCream()" />
.
.
.
methods: {
CatchIceCream() {
console.log('not good for children');
}
},
Never mind! I was able to figure it out based on the answer given here. I had to $emit() event directly to the $parent like the following:
this.$parent.$emit('icecream');
While this could be a bit problematic in different scenarios, it's completely fine on my case.

Vuetable2 slot in slot

I use vue(2.6.10) an Im trying to build a universal table with vuetable2 (2.0.0-beta.4).
I created a component for the general methods of vuetable.
I tried to place my "MyCustomTemplate" in the slot section of the "MyVueTable", but I got no error and nothing is shown.
My goal is to use the "MyVueTable" in other vue pages and replace the "MyCustomTemplate".
I have currently 3 entries in my data but in the List.vue component nothing is shown
List.vue
<template>
<MyVueTable :data="data" :fields="fields">
<MyCustomTemplate v-slot="vueTableTemplateSlot"/>
</MyVueTable>
</template
<script>
export default {
name:"List",
data(){
return{
data: [],
fields: [
{
name: 'vueTableTemplateSlot'
}
]
};
}
}
</script>
MyVueTable.vue
<template>
<vuetable ref="vuetable">
<slot name="vueTableTemplateSlot" slot-scope="props"/>
</vuetable>
</template>
<script>
export default {
name: 'MyVueTable',
props: ['data', 'fields'],
methods:{
//vuetable methods
}
}
</script>
MyCustomTemplate.vue
<template>
<div>
{{rowData.id}}
</div>
</template>
<script>
export default {
name: 'MyCustomTemplate',
data(){
return{
rowData: null
}
}
</script>
You can test to put your component(in List.vue) in a div or a template that will be the slot content :
<template #nameOfYourSlot>
<NameOfYourComponent>
</template>
This was answered in the official repository, you need to do this to be your custom global component: https://github.com/ratiw/vuetable-2-tutorial/wiki/lesson-17

Existing component throw: Unknown custom element

I'm trying to use a component in another component. On the created event, I can log this component and it returns the good object. However for some reasons, the component doesn't seem to be included. VueJS do not understand the validation tag.
Any ideas?
<template>
<main>
<validation :validate="$v.email" :model="'email'"></validation>
</main>
</template>
<script>
import { Validation } from 'components/helpers'
export default {
name: 'login',
component: { Validation },
created() {
// it works. print the component with his path
window.console.log(Validation)
}
}
</script>
[Vue warn]: Unknown custom element: - did you register
the component correctly? For recursive components, make sure to
provide the "name" option.
In components/helpers I have two file:
1) index.js
export { default as Validation } from './Validation'
2) Validation.vue
<template>
<div>
<span class="form__validation" v-if="validate && !validate.required">Required</span>
<template v-if="validation[model]">
<span class="form__validation" v-for="error in validation[model].messages">{{ error }}</span>
</template>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'validation',
data() {
return {
L: L
}
},
props: ['model', 'validate'],
computed: {
...mapGetters({
validation: 'getValidation'
})
}
}
</script>
Changing component for components did the trick. Shame on me :)