render component does not work when using v-for vuejs - vue.js

I want to make dynamic components fetched from an array. I tried using <component></component> to achieve this, but it didn't render anything. When I inspect it in the browser, the component is rendered like below:
<test></test>
My code is,
<template>
<component v-for="(section, index) in compo" :key="section+index" v-bind:is="section" title="Gd" />
<template>
<script>
import Test from "#/Components/Test.vue";
export default {
data() {
return {
compo: ['Test']
}
},
};
</script>

Related

Vue 3: Each button click, generate new component and append as child in the target element

I have been searching for a simple solution to generate components in a Vue 3 app programmatically. So far, I've used defineComponent to extend the div component and attach it to the main component via createApp and mount:
Main Component
<template>
<button type="button" v-on:click="addDiv"></button>
<div id="app-main">
</div>
</template>
<script>
import {defineComponent, createApp} from 'vue'
import Div from './components/Div.vue'
export default{
name: 'App',
methods: {
addDiv: () => {
let newDiv = defineComponent({extends: Div});
createApp(newDiv).mount("#app-main");
}
}
}
</script>
Div Component:
<template>
<div>This is a div</div>
</template>
<script>
export default {
name: 'Div'
}
</script>
My issue with this is that mount replaces everything in the target element. If you click the button 3 times, only 1 div appears instead of 3. I need a method where the code appends the component as a child in the target element allowing me to create as many div components as I want. Thanks in advance!
Don't use mount as that is designed to mount a new Vue instance. What you want to use is the <component :is="component_name"> feature and possibly have an array or other data structure containing the list of components you wish to have rendered.
For instance, consider the following simple example:
Vue.component('my-component', {
// component config goes here, omitting for simplicity.
});
new Vue({
el: '#app',
data: {
elements: []
},
methods: {
addNewDiv() {
this.elements.push({type: 'my-component'});
}
}
});
<div id="app">
<component v-for="element in elements" :is="element.type"></component>
</div>
This will iterate over the elements array dynamically and will replace each instance of the <component> tag with whatever is defined by the array element.

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>

parent component is not getting data from child in Nuxt app

This is driving me crazy so I hope that anyone can help.
I made a Nuxt app with #nuxt/content and I'm using Netlify-CMS to create content. That all seems to work fine. However I'm trying to display a component that contains a loop of the MD-files that I have, but in the index.vue nothing of the loop is displayed.
I know (a little) about props and $emit, but as I am not triggering an event this dosen't seem to work.
Component code:
<template>
<section>
<h1>Releases</h1>
<li v-for="release of rfhreleases" :key="release.slug">
<h2>{{ release.artist }}</h2>
</li>
</section>
</template>
<script>
export default {
components: {},
async asyncData({ $content, params }) {
const rfhreleases = await $content('releases', params.slug)
.only(['artist'])
.sortBy('createdAt', 'asc')
.fetch()
return {
rfhreleases,
}
},
}
</script>
And index.vue code:
<template>
<div>
<Hero />
<Releases />
<About />
<Contact />
</div>
</template>
<script>
export default {
head() {
return {
script: [
{ src: 'https://identity.netlify.com/v1/netlify-identity-widget.js' },
],
}
},
}
</script>
If I place my component code as part of index.vue, everything work, but I would love to avoid that and thats why I'm trying to place the loop in a component.
As stated on the Nuxt documentation:
This hook can only be placed on page components.
That means asyncData only works on components under pages/ folder.
You have several options:
You use fetch instead. It's the other asynchronous hook but it's called from any component. It won't block the rendering as with asyncData so the component it will instanciated with empty data first.
You fetch your data from the page with asyncData and you pass the result as a prop to your component
<template>
<div>
<Hero />
<Releases :releases="rfhreleases" />
<About />
<Contact />
</div>
</template>
<script>
export default {
async asyncData({ $content, params }) {
const rfhreleases = await $content('releases', params.slug)
.only(['artist'])
.sortBy('createdAt', 'asc')
.fetch()
return {
rfhreleases,
}
},
}
</script>

How to import a component as prop from another component?

It is possible to import a component as prop from another component?
For example:
Q-Dialog
<template>
<q-dialog>
<q-layout>
<q-page-container>
<myCustomComponent />
</q-page-container>
</q-layout>
</q-dialog>
</template>
<script>
//Edited:This works, but I want to register dynamically from props
//import myCustomComponent from "components/MyCustomComponent.vue";
import myCustomComponent from this.myComponent;
export default {
props: ["myComponent"],
components: { myCustomComponent }
}
Another component:
this.$q.dialog({
component: CustomComponent, //dialog
myComponent: 'components/MyCustomComponent.vue'
})
Edited, for better clarify what I am trying to achieve in this case:
My dialog is an abstract component in which an unlimited number of different myCustomComponent can be rendered.
To achieve this, I need that the registration of each component (import) is not done in the q-dialog.
A solution to consider is to register each component in the file from which the q-dialog is loaded for rendering (different from the q-dialog, in my case the another component file) and then pass that path from the imported file to the q-dialog, possibly as props.
Is this possible?
Edited with solution:
Parent component
<script>
import registeredComponent from "components/MyCustomComponent.vue";
export default {
data() {
return {
myComponent: registeredComponent
}
}
methods: {
btnClickShowDialog(){
this.$q.dialog({
component: dialogComponent,
//pass registered component as prop to dialog
myCustomComponent: this.myComponent
})
}
}
</script>
Q-dialog
<template>
<q-dialog>
<q-layout>
<q-page-container>
<component :is="myCustomComponent" />
</q-page-container>
</q-layout>
</q-dialog>
</template>
<script>
export default {
props: ["myCustomComponent"]
}
</script>
In your q-dialog component you can use the component tag to dynamically render a passed in component prop. See this stackblitz.
// q-dialog html
<component :is="myComponent" />
In your parent component you'll want to import the desired component, assign it to a data property and pass it in
// parent component js
import SomeComponent from './SomeComponent.vue'
data () {
return {
passedInComponent: SomeComponent
}
}
// parent component html
<q-dialog :my-component="passedInComponent" />

Vue.js Generic Dynamic Component Rendering

I have a component that dynamic render components that get as props
<template>
<div>
<component :is="component" :data="data" v-if="component" />
</div>
</template>
<script>
export default {
name: 'dynamic-component-renderer',
props: ['data', 'type'],
computed: {
component() {
if (!this.type) {
return null;
}
return this.type;
},
}
}
</script>
The issue is in imports, I need to dynamic import, I know that I can do dynamic importing with webpack like this: () => import('./my-async-component'), but in my case, I don't need lazyLoad.
So I need to have a generic dummy component (dynamic-component-renderer) that will not know what components will get and dynamically render.