I have a nested structure with vue:
<app>
<column> : [array]
<task> : [array]
<resource> : [array]
I'm also using single file components.
For <app> component it looks like this (/App.vue file)
<template>
<div id="app">
<column v-for=""></column>
</div>
</template>
<script>
import column from './components/Column'
export default {
name: 'project',
components: {
column
},
data () {
return {
}
}
}
</script>
The <column> component (/components/Column.vue file):
<template>
<div>
<task v-for=""></task>
</div>
</template>
<script>
import task from './Task'
export default {
name: 'column',
components: {
task
},
data () {
return {
}
}
}
</script>
The <task> component (/components/Task.vue file):
<template>
<div>
<resources></resources>
</div>
</template>
<script>
import { resources } from './Init-resource'
export default {
name: 'task',
components: {
resources
},
data () {
return {
}
}
}
</script>
Up until this point everything works flawlessly. The app renders a bunch of columns, and inside the columns it renders tasks.
But then comes another layer <resource>, that looks like this (/components/Init-resource.vue file):
<template>
<div>
<select>
<option v-for=""></option>
</select>
</div>
</template>
<script>
export default {
name: 'resources',
data () {
return {
}
}
}
</script>
I get this error:
vue.common.js?e881:509 [Vue warn]: Unknown custom element: <resources> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
(found in component <task>
It doesn't recognize the last <resource> component. But everything looks fine, imports are correct, components are returned for the template...
I found it. I used named import, so instead of this:
import { resources } from './Init-resource'
it should be this:
import resources from './Init-resource'
Related
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.
Using nuxtjs/svg package, I'm conditionally rendering inline SVGs thus:
<ArrowRight v-if="condition" />
<ExternalLink v-else />
Script:
import ArrowRight from '~/assets/img/arrow-right.svg?inline'
import ExternalLink from '~/assets/img/external-link.svg?inline'
export default {
components: {
ArrowRight,
ExternalLink
}
}
I'd like to make these imports dynamically, but I don't know how in this case partly because of the necessity of the "?inline" part when importing the SVG.
Any idea as to how I can import the SVGs dynamically?
<template>
<div v-html="require(`~/assets/img/${image}.svg?raw`)"/>
</template>
<script>
export default {
computed: {
image() {
return condition ? 'arrow-right' : 'external-link'
}
}
}
</script>
That's one way by using SVGs as raw. But the idea should be clear ;-)
(copied from nuxt svg module readme)
To dynamically import an SVG, you can use the inline require() syntax.
<template>
<div v-html="require(`../assets/${name}.svg?raw`)" />
</template>
<script>
export default {
props: {
name: { type: String, default: "image" },
},
};
</script>
To render an SVG without wrapper element and the use of v-html, a combination of dynamic components and ?inline can be used.
<template>
<component :is="require(`../assets/${name}.svg?inline`)" />
</template>
<script>
export default {
props: {
name: { type: String, default: "image" },
},
};
</script>
I am trying to include one component into another component, but I am getting the error "Failed to resolve component: applications-overview-table" in the browser console.
Parent component "src/pages/ApplicationsOverview.vue":
<template>
<q-page class="flex flex-center">
<applications-overview-table></applications-overview-table>
</q-page>
</template>
<script>
import ApplicationsOverviewTable from '../components/application/OverviewTable.vue';
export default {
name: 'ApplicationsOverviewPage',
components: [ApplicationsOverviewTable],
setup() {
console.log('loading applications overview');
return {};
},
};
</script>
<style></style>
Child component "src/applications/OverviewTable.vue":
<template>
<div class="q-pa-md">
<q-table title="Aanvragen" :rows="rows" :columns="columns" row-key="name" />
</div>
</template>
<script>
const columns = [ ... ];
const rows = [ ... ];
export default {
name: 'ApplicationsOverviewTable',
setup() {
return {
columns,
rows,
};
},
};
</script>
I can see that the parent component is being loaded, because the console message "loading applications overview" is being shown in the console.
I can also see that the path to OverviewTable.vue is correct, because if I change the path I get another error.
I tried to change <applications-overview-table> to <ApplicationsOverviewTable> but this gives me the same error (with the tag-name different of course).
It is right that I should change the CamelCase component name to dash-case in the templete, isn't it?
What am I doing wrong?
components option has an object as value not an array, it should be :
components: {ApplicationsOverviewTable},
this a shorthand of :
components: {
ApplicationsOverviewTable : ApplicationsOverviewTable
},
I have this code in file app.vue :
<template>
<div id="app">
<button v-on:click="component = 'login'">aa</button>
<component v-bind:is="component"></component>
</div>
</template>
<script>
import acceuil from './components/acceuil.vue'
import login from './components/login.vue'
export default {
name: 'app',
components: {
acceuil,
login
},
data(){
return {
component: 'acceuil'
}
}
}
</script>
How can I toggle between acceuil/login in component from a different vue file ?
You need to pass the imported dependency (the object or the name of the component as a string) to v-bind:is. You can do this by returning it in a computed function and pass it to a computed property, which you then can use in the template.
<template>
<div id="app">
<button v-on:click="isLogin = true">Show Login</button>
<component v-bind:is="currentComponent"></component>
</div>
</template>
<script>
import acceuil from './components/acceuil.vue';
import login from './components/login.vue';
export default {
name: 'app',
data () {
return {
isLogin: false
};
},
computed: {
currentComponent () {
return this.isLogin ? login : acceuil;
}
},
};
</script>
See also the documentation of dynamic components in the official docs.
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 :)