Can I create varying number of component based on length of array? - vuejs2

Suppose I want to create number of components based on the length of the array which props from the parent component.
<template>
<------here is some logic to toggle "ComponentName"------->
<keep-alive>
<component v-bind:is="ComponentName"></component>
</keep-alive>
</template>
<script>
export default {
components: {},
methods: {
createComponents: function(){
for (let i = 0; i < this.MyArray.length;i++){
----Create component with name "Component-i" and based on the content in MyArray----
}
}
}
data(){
return{
MyArray: this.$route.params.MyArray,
ComponentName: '',
},
}
}
</script>
Is there are any way to do so? I mean, to create dynamic number of component?

Related

Vue.js how to check if inputs from child components are filled so parent component can able/disable a button

Hi I'm new to vuejs and I'm struggling figuring it out how to make this work.
I have 3 different child components in a parent component, each one of the child components have multiple text and radio inputs. What I want to acomplish is to be able to disable a button on the parent component if there are empty inputs or not selected radio buttons.
Can someone explain to me how could I approach this? Thank you!.
You are looking for how to emit data, opposite to passing data down through a prop.
Here is a small example for an input field.
Child1.vue
<template>
<p>
Write something:
<input v-model="inputText" />
</p>
<p>{{ inputText }}</p>
</template>
<script>
export default {
data() {
return {
inputText: '',
};
},
emits: ['emitInput'],
watch: {
inputText() {
this.checkContent();
},
},
methods: {
checkContent() {
this.$emit('emitInput', this.inputText === '');
},
},
};
</script>
App.vue (parent):
<template>
<div id="app">
<button :disabled="disabledButton">Parent Button</button>
<Child1 #emitInput="parentMethod" />
</div>
</template>
<script>
import Child1 from './Child1.vue';
export default {
name: 'App',
components: {
Child1,
},
data() {
return {
disabledButton: true,
};
},
methods: {
parentMethod(payload) {
//Since you had a 2nd parameter on line 24 in Child1, you can access it.
//We were checking if input is empty or not, returning a boolean.
this.disabledButton = payload;
},
},
};
</script>

How to wrap the slot in a scoped slot at runtime

I have an very unusual scenario.
<WrapperComponent>
<InnerComponent propA="Static"></InnerComponent>
</WrapperComponent>
The WrapperComponent should manage all instances of the InnerComponent. However I don't know what will be compiled into the wrapper component.
Usually I would do something like this:
<WrapperComponent>
<template scoped-slot="{data}">
<InnerComponent v-for="(propB) in data" prop-b="Static" :prop-b="propB"></InnerComponent>
</template>
</WrapperComponent>
But I cannot do this for reasons!
I need to be able to create multiple instances of the slot content at runtime. I have created a code sandbox with what I got so far.
https://codesandbox.io/s/stupefied-framework-f3z5g?file=/src/App.vue:697-774
<template>
<div id="app">
<WrapperComponent>
<InnerComponent propA="Static"></InnerComponent>
</WrapperComponent>
</div>
</template>
<script>
import Vue from "vue";
const WrapperComponent = Vue.extend({
name: "WrapperComponent",
data() {
return {
valueMap: ["Child 1", "Child 2"]
}
},
render(h) {
return h("div", {}, [
this.valueMap.map(val => {
console.log(val);
for (const slot of this.$slots.default) {
// this is a read only slot. I can not change this.
// However, I want multiple instances of the slot
// => Inject a scoped-slot
slot.componentOptions.propsData.propB = val;
}
return h("div", {}, [this.$slots.default]);
})
])
}
});
const InnerComponent = Vue.extend({
name: "InnerComponent",
props: {
// This is a static configuration value.
propA: String,
// This is a runtime value. The parent component manages this component
propB: String
},
template: "<div>A: {{propA}} B: {{propB}}</div>"
});
export default {
name: "App",
components: {
WrapperComponent,
InnerComponent
}
};
</script>
This works perfectly fine with static information only, but I also have some data that differs per slot instance.
Because VNodes are readonly I cannot modify this.$slot.default.componentOptions.propsData. If I ignore the warning the result will just be the content that was passed to last instance of the slot.
<WrapperComponent>
<WrapperSubComponent v-for="(propB) in data" key="probB" :prop-b="prop-b">
<InnerComponent propA="Static"></InnerComponent>
</WrapperSubComponent>
</WrapperComponent>
Works after wrapping the component in another component and only executing the logic once.

Vuejs build/render component inside a method and output into template

I have a string (example, because it's an object with many key/values, want to loop and append to htmloutput) with a component name. Is it possible to render/build the component inside a method and display the html output?
Is that possible and how can i achieve that?
<template>
<div v-html="htmloutput"></div>
</template>
<script>
export default {
component: {
ComponentTest
},
data() {
return {
htmloutput: ''
}
},
methods:{
makeHtml(){
let string = 'component-test';//ComponentTest
//render the ComponentTest directly
this.htmloutput = ===>'HERE TO RENDER/BUILD THE COMPONENTTEST'<==
}
},
created(){
this.makeHtml();
}
</script>
You might be looking for dynamic components:
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
Example:
<template>
<component :is="changeableComponent">
</component>
</template>
<script>
import FirstComponent from '#/views/first';
import SecondComponent from '#/views/second';
export default {
components: {
FirstComponent, SecondComponent
},
computed: {
changeableComponent() {
// Return 'first-component' or 'second-component' here which corresponds
// to one of the 2 included components.
return 'first-component';
}
}
}
</script>
Maybe this will help - https://forum.vuejs.org/t/how-can-i-get-rendered-html-code-of-vue-component/19421
StarRating is a sample Vue component. You can get it HTML code by run:
new Vue({
...StarRating,
parent: this,
propsData: { /* pass props here*/ }
}).$mount().$el.outerHTML
in Your method. Remember about import Vue from 'vue'; and of course import component.
What you're trying to do really isn't best practice for Vue.
It's better to use v-if and v-for to conditionally render your component in the <template> section.
Yes you can use the render function for that here is an example :
Vue.component('CompnentTest', {
data() {
return {
text: 'some text inside the header'
}
},
render(createElement) {
return createElement('h1', this.text)
}
})
new Vue({
el: '#app',
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<Compnent-test />
</div>
Or :
if you are using Vue-cli :
on your componentTest component :
export default {
render(createElement) {
return createElement('h1', 'sometext')
}
// Same as
// <template>
// <h1>sometext</h1>
// </template>
//
}
and on your root element (App.vue as default) :
export default {
....
component: {
ComponentTest
}
}
<template>
<div>
....
<Component-test />
</div>
</template>
example : codesandbox
you can read more about
Render Functions & JSX

Dynamically import components based on array item values

I've got a single page app and im trying to import and render components based on what components exists in array(blockCount). Im storing several strings(component name) in said array.
Vue:
computed: {
componentInstance () {
for(var i = 0; i < this.blockCount.length; i++){
return () => import(`#/components/${this.blockCount[i]}`)
}
}
},
Html:
<component v-for="component in blockCount" :is="componentInstance" />
So the issue im having is that the function stop at item[0] and just iterates that item. And im not quite sure how to iterate this function in a dynamic way.
You might want to define a child component
ChildComponent.vue
<template>
<component :is="componentInstance" />
</template>
<script>
export default {
props: ['componentName'],
computed: {
componentInstance() {
return () => import(`#/components/${this.componentName}`)
}
}
}
</script>
and render in parent:
<child-component v-for="(component, index) in blockCount" :key="index" :componentName="component"/>

VUE/NUXT: Pass random number as props to child component

I want to have a randomNumber variable inside a vue component. But in my template and in my script tag, the values never match.
using datas:
<template>
<div :data-number="randomNumber"
</template>
<script>
export default {
data (){
return { randomNumber: Math.random() * 1000}
},
mounted: function(){
console.log(this.randomNumber)
}
}
</script>
using computed property:
<template>
<div :data-number="randomNumber"
</template>
<script>
export default {
computed{
randomNumber: Math.random() * 1000
},
mounted: function(){
console.log(this.randomNumber)
}
}
</script>
Or passing the randomNumber as props from my parent component.
I expect data-number to be equal to this.randomNumber, but it's never the case.
I managed to get a workaround for my project, but I'm still interested in the solution here.
Any help will be much appreciated, thanks !
The problem is that you need to first initialize randomNumber variable number as null, then assign a value in the mounted() method like:
Main Component
<template>
<div id="app">
{{randomNumber}}
<ChildComp :randomNum="randomNumber"/>
</div>
</template>
<script>
import ChildComp from './components/ChildComp'
export default {
name: "App",
data (){
return {
randomNumber: null
}
},
mounted: function(){
this.randomNumber = Math.random() * 1000
},
components: {
ChildComp
}
};
</script>
Child Component
<template>
<div>{{randomNum}}</div>
</template>
<script>
export default {
name: "ChildComp",
props: {
randomNum: Number
}
};
</script>
This gives the same output every time like
190.9193234159494
190.9193234159494
Because you don't know how many times your data or computed functions trigger and initialize the variable.