How to import two VueJS components inside eachother? - javascript

I have two components, an item component and a folder component. Each item contains it's own folder component, and each folder component contains a list of items.
So I am trying to use the item component in the folder component, and vice versa, however, I am getting the error: unknown custom element: <item> - did you register the component correctly? For recursive components, make sure to provide the "name" option. Despite the fact that I have the name option set on both components.
Any ideas?
Code is below
item.vue
<template>
<div class="item" style="height: 30px">
. . .
<folder v-if="item.hasChildren" :id="id"></folder>
</div>
</template>
<script scoped>
import Folder from './folder.vue';
export default {
name: 'item',
props: ['id'],
components: {
Folder
}
};
</script>
folder.vue
<template>
<div class="folder">
<template v-for="(child, index) in children">
<item :last-item="index === children.length - 1" :key="child.id" :id="child.id"></item>
</template>
</div>
</template>
<script>
import Item from "./item.vue";
export default {
name: 'folder',
props: ['id', 'hasChildren'],
components: {
Item
}
};
</script>

This is probably happening because of the circular referencing between your components ..
When you look closely, you’ll see that these components will actually
be each other’s descendent and ancestor in the render tree - a
paradox!
To resolve this paradox you can either register your components globally using Vue.component, or you can defer the import of one of your components to a later point (by moving your import to the beforeCreate hook or using async components as demonstrated here) ..
folder.vue
<template>
<div class="folder">
<template v-for="(child, index) in children">
<item :last-item="index === children.length - 1" :key="child.id" :id="child.id"></item>
</template>
</div>
</template>
<script>
export default {
name: 'folder',
props: ['id', 'hasChildren'],
components: {
'item': () => import('./item.vue')
}
};
</script>

You need to provide the key (name) in the components object like so,
item.vue
components: {
folder: Folder
}
folder.vue
components: {
item: Item
}

Related

How to import dynamic v-bind:is component with module system in Vue

Problem: I am trying to programmatically register a component to be used in a slot in my Vue/Nuxt site. The component name is included in the data of the parent index.vue file, in this instance the component is named Projects. I am including it in a v-for template as the various objects in the 'modules' data array are iterated over. I had assumed this would be possible/easy from the dynamic component documentation and example however I have not managed to get it working in my case.
What I expect to happen: I expected the component to be registered and 'slotted' into the Module component correctly.
What actually happens: While I can see on the rendered view that the component is 'there', it is not in the correct place (i.e. slotted into the Module component). I also get a vue/no-unused-components error saying: The "Projects" component has been registered but not used.
I have read the documentation about component registration in modular systems but these seem to be for more complex cases than what I am trying to achieve. Any advice would be really helpful as I am totally stuck!
index.vue
<template>
<div>
<template v-for="module in modules">
<Module
:title="module.title"
:to="module.link"
/>
<component v-bind:is="module.slot" />
</Module>
</template>
</div>
</template>
<script>
import Module from '~/components/module/Module.vue'
import Projects from '~/components/module/slots/Projects.vue'
export default {
components: {
Module,
Projects
},
data () {
return {
modules: [
{
title: 'Work',
slot: 'Projects'
},
{
...
}
]
}
}
}
</script>
Edit: As a side note, I get the same error when registering the component with import like so:
components: {
Module,
'Projects': () => import('#/components/module/slots/Projects')
}
Module.vue
<template>
<div>
<h2>
{{ title }}
</h2>
<slot />
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
default: ''
}
}
}
</script>
Projects.vue
<template>
<div>
<h3>Projects</h3>
</div>
</template>
<script>
export default {
name: 'Projects'
}
</script>
You use the self closure tag in your Module component.
This prevents your Projects component to be rendered within the slot.
Just replace:
<Module
:title="module.title"
:to="module.link"
/>
with:
<Module
:title="module.title"
:to="module.link"
>
and it should work.

How Can I Know In Which Component I Was There

I am working with vue js, and I have dargable component items, I generated dragable component using for loop to display component, so how can i detected in which component I am there, when click on component.
This is my index.vue file :
import item from './component/itemComponent.vue';
<template>
<div>
<dragable
:element="ul"
v-modal="list">
<li v-for="(index, i) in list" :key="i">
<item :indexItem="index"></item
</li>
</dragable>
</div>
</template>
And this is itemComponent.vue file :
<template>
<a
#click="getComponentAttributes()"
class="btn btn-primary">{{ intemIndex.name }} </a>
</template>
<script>
export default {
props:['indexItem'],
data(){
return {
isOpen: false
}
},
methods : {
getComponentAttributes(){
this.isOpen = true;
// its not working just for one
// component, it working for all component and I need
// to implment to one component specific
// current component
}
}
}
</script>
Edited following comments.
A bit complicated but does the trick. https://jsfiddle.net/guillaumedeslandes/t12k43ov/
Added events to trigger a forced-close state, and had to store the item state in the parent component to allow draggable to manipulate it while keeping the order.

Vue.js - append variable to component

I am currently using Algolia instantsearch for Vue, which is working fine. However, I am using multiple indexes, so I would like to be able to set a custom Title to each of my indexes.
This is my files:
Home.vue:
<ais-index :appId="angoliaAppId" :apiKey="angoliaAppKey" indexName="contacts" :query="query">
<search-results></search-results>
</ais-index>
<ais-index :appId="angoliaAppId" :apiKey="angoliaAppKey" indexName="users" :query="query">
<search-results></search-results>
</ais-index>
import SearchResults from "../search/Results";
export default {
data() {
return {
query: '',
angoliaAppId: process.env.MIX_ALGOLIA_APP_ID,
angoliaAppKey: process.env.MIX_ALGOLIA_SECRET,
};
},
components: {
SearchResults
}
};
And my component file, looks like this:
../search/Results:
<template>
<ais-results class="ais-Hits__root" v-if="searchStore.query.length > 0" :data="category">
<template slot-scope="{ result }">
<div>
<ais-highlight :result="result" attribute-name="name"></ais-highlight>
</div>
</template>
</ais-results>
</template>
import SearchResults from "../search/Results";
import {
Component
} from "vue-instantsearch";
export default {
mixins: [Component]
};
Now, as said, I would very much like to be able to set a custom title name to each of my <search-results> components, like this:
<search-results :title="My Contacts"></search-results>
Which would then for example add this in the component file:
<h1>{{ title }}</h1>
Seems like you'll have to use props
https://v2.vuejs.org/v2/guide/components.html#Passing-Data-to-Child-Components-with-Props

print Component B inside component A - vue.js

Using Vue.js,
How to create componentA that gets componentB as a prop, and print it inside of it?
example:
index.vue
<template>
<div>
<componentA :componentPlaceHolder="componentB"></componentA>
</div>
</template>
<script>
import componentA from './compnentA.vue';
import componentB from './componentB.vue'
export default {
name: 'index',
components: {componentA,componentB }
}
</script>
componentA.vue
<template>
<div>
{{componentPlaceHolder}}
</div>
</template>
<script>
export default {
name: 'componentA',
props: {
'componentPlaceHolder': {}
}
}
</script>
There are some issues to your implementation:
You have gotten the scope wrong: componentPlaceHolder lives in the parent scope, not in that of component A. Read: Compilation Scope.
Use :is (i.e. v-bind: is) for dynamic component binding. The data bound should reference the key of the component.
Since you are nested additional components in another component in the same context, that means you have to interweave the content. This is done by using slots, declared in <component-a>.
Avoid using case-sensitive DOM elements, use kebab case instead, i.e. <component-a> instead of <componentA>, since HTML elements are case-insensitive (<componentA> and <componenta> will be treated the same).
Here is the updated code:
<template>
<div>
<component-a>
<customComponent :is="componentPlaceHolder"></customComponent>
</component-a>
</div>
</template>
<script>
import componentA from './componentA.vue';
import componentB from './componentB.vue'
export default {
name: 'index',
components: {
'component-a': componentA,
'component-b': componentB
},
data: {
componentPlaceHolder: 'component-b'
}
}
</script>
And then in your componentA.vue:
<template>
<div>
<!-- Slot will interweave whatever that is found in <componentA> -->
<slot></slot>
</div>
</template>
<script>
export default {
name: 'componentA'
}
</script>
Proof-of-concept example
If in doubt, here is a live proof-of-concept example:
var componentA = {
template: '#component-a'
};
var componentB = {
template: '#component-b'
};
new Vue({
el: '#app',
components: {
'component-a': componentA,
'component-b': componentB
},
data: {
componentPlaceHolder: 'component-b'
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="app">
<component-a>
<!-- DOM elements in here will be interweaved into <slot> -->
<customComponent :is="componentPlaceHolder"></customComponent>
</component-a>
</div>
<template id="component-a">
<div>
<p>I am component A</p>
<slot></slot>
</div>
</template>
<template id="component-b">
<p>I am component B</p>
</template>
Footnote:
The VueJS readme is exceptionally composed, and I suggest here are some things that you can read up on that is very relevant to your use case:
Compilation Scope
Dynamic Components
Content Distribution with Slots

vue.js passing data from parent single file component to child

Using single file architecture I'm trying to pass data (an object) from a parent component to a child:
App.vue
<template>
<div id="app">
<app-header app-content={{app_content}}></app-header>
</div>
</template>
<script>
import appHeader from './components/appHeader'
import {content} from './content/content.js'
export default {
components: {
appHeader
},
data: () => {
return {
app_content: content
}
}
}
</script>
appHeader.vue
<template>
<header id="header">
<h1>{{ app_content }}</h1>
</header>
</template>
<script>
export default {
data: () => {
return {
// nothing
}
},
props: ['app_content'],
created: () => {
console.log(app_content) // undefined
}
}
</script>
Seems to be such a trivial task and probably the solution is quite simple. Thanks for any advice :)
You're almost there.
In order to send the app_content variable from App.vue to the child component you have to pass it as an attribute in the template like so:
<app-header :app-content="app_content"></app-header>
Now, in order to get the :app-component property inside appHeader.vue you will have to rename your prop from app_component to appComponent (this is Vue's convention of passing properties).
Finally, to print it inside child's template just change to: {{ appContent }}

Categories