Dynamic component inside a component that is rendered with render() function - vue.js

I saw in the docs that you can have a dynamic component inside your VueComponent like this :
<component :is="componentName" v-bind="componentProps" #custom-event="doSomething" />
I am trying to have one of these dynamic components inside a dynamically rendered component (with the render() function, not with an HTML template). Without too much hope, I've tried this :
render(createElement: CreateElement) {
return createElement('component', props: {
'is': 'TestComponent'
});
}
but I got
[Vue warn]: Unknown custom element: <component> - did you register the component correctly?
So again, not hoping too much for a miracle, I tried to import Component and declare it as a component :
#Component({
components: {
Component,
TestComponent
}
})
export default class DynamicThingy extends Vue {
render(createElement: CreateElement): VNode {
return createElement('Component', {
props: {
'is': 'TestComponent'
}
});
}
}
But then I get
[Vue warn]: Do not use built-in or reserved HTML elements as component id: Component
Any idea how that could be possible ?

The first parameter of createElement() must be either
An HTML tag name,
component options,
or async function resolving to one of these.
https://v2.vuejs.org/v2/guide/render-function.html#createElement-Arguments
So in a render function, you can simply create a function* that returns one or another component's options based on your desired criteria, and let that be your first argument. *This function is identical to the function you'd write to determine what goes into the :is prop)
You only need the dynamic component <component /> and :is prop in a template where you don't have the possibility to do it programmatically.
You can use this smart-list component from the vue docs as an example.

Related

Vue Test Utils: how to pass Vuelidate validation rules to child components?

while trying to write a component test by using vue test utils, testing interaction between child components and stuff, I am stuck due to usage of Vuelidate from child components. Below is an example simplified:
// parent component code
<template>
<div>
<childA />
</div>
</template>
//childA code
<template>
<input v-model="value" />
</template>
<script>
...
validations: {
value: {
required
}
}
...
</script>
// parent component test
...
const wrapper = mount(MyParentComponent, {
...,
components: {
childA,
},
validations: {
value: required
},
...
})
I have tried to find a solution out there that I could mount (note here that I WANT to mount also the child components, so shallow-mount is not what I look for) the child component, with it's respective Vuelidate validation rules, but I still haven't found any solution.
Instead, my test gives me errors like:
Cannot read property `value` of undefined
which makes sense, since the test cannot access the child component's $v instance.
Has anyone achieved it so far?
For answering your question and after i've did some test i believe you missed the data part inside your mount
mount: render child components
shallowMount: doesn't render child components
MyParentComponent need to have in the options the structure of you're child component so this is why he is returning the error
And i saw that you're passing the import of your component directly but don't forget that your test folder is outside of your src folder
import ChildA from "#/components/ChildA";
will not work instead i propose to use absolute path directly to import your child component or use a configuration to resolve them
const wrapper = mount(MyParentComponent, {
data() {
return {
value: null
}
},
components: {
ChildA: () => import('../../src/components/ChildA'),
},
validations: {
value: required
},
})

Trigger child function in parent component

I have a component Base.vue with a child Component that is being displayed by router-view. In the child component I have a function in my setup(), but I want the function to be triggered by menu item in Base.vue. How can I achieve this in vue 3? I can't use vuex for this, because the function uses a lot of google map things and refs.
Base.vue router-view Component <- component contains another component which I need to use the function from in Base.vue
This can be easily implemented through the Vue Composition Api
Assign a ref to your child component as such
<child-component ref="childRef"></child-component>
Declare the ref in your setup function as
import { ref } from 'vue'
export default {
components: { },
props: {},
setup(context,props) {
const childRef = ref(null)
return {childRef}
}
}
You can now call functions in your child component by leveraging your childRef, inside the setup function childRef.value.childFunction() can call functions declared in your child component

How to pass an imported function(not method) as a prop

when passing dynamic props, we use : or v-bind. However, any variables within these v-binds refers to the data stored within the component rather than within the script (passes on data in the component that can be referenced using "this.data".
However, that is not what I want to do in this case, I want to pass down an imported function into a prop, which is referenced within the component without a "this." in front of the function. When I try to pass it on as a prop, it says "functionName" is defined but never used", how do you go about passing on imported functions as dynamic props? See the code below for more details.
<template>
<componentName
:propName="functionName"
/>
</template>
<script>
import {functionName} from "./helperFunctions.js"
export default {
components: {componentName}
}
why not passing as an event? however you can pass this function as a prop via getters:
<template>
<componentName
:propName="myFuncProp"
/>
</template>
<script>
import {functionName} from "./helperFunctions.js"
export default {
components: {componentName},
computed: {
myFuncProp () {
return functionName
}
}
}
</script>

How dynamic & async component in VueJS receive its props?

While reading the excellent documentation of VueJS, I didn't understand how dynamic async component can get its props from its outer component.
Is that possible? How?
They do have a great example here.
What if my component has props like "something" need to be passes and render:
<component :is="my-component"></component>
Vue.component('my-component', {
template: '<div>{{something}}</div>',
props: { something: String }
})
Doing something like this will not work:
<component :is="my-component" :something="'text'"></component>
:something is specific name of prop, but the component not always known, so its props are also not known. This is the senario why using so it can by any component.

How to create a reusable component in VueJs?

I would like to create Side Panel as a reusable component in Framework7 with VueJS. Here is my code..
Card.vue
<f7-card>
<f7-card-header>Card header content</f7-card-header>
<f7-card-content><img src="https://wiki.videolan.org/images/Ubuntu-logo.png"></img></f7-card-content>
<f7-card-footer>Card footer content</f7-card-footer>
</f7-card>
Now i registered as a component like below,
import Vue from 'vue'
export default [
{
path: '/card/',
component: require('./Card')
},
]
In later vues i imported as,
import Card from './Card.vue'
and i try to access in another vues like,
Now i'm getting an error like
[Vue warn]: Unknown custom element: - did you register the
component correctly? For recursive components, make sure to provide
the "name" option.
Can anyone help me where have i mistaken?
Thanks,
It's not really clear from your code exactly what you are doing, but the error you are getting happens when you try to use a component as a child of another component without registering it in the parent's components setting like this:
<script>
import Card from './Card.vue'
export default {
data () {
return {
somedata: 'some value'
}
},
components: {Card: Card}, // <- you're missing this
// etc...
}
</script>
You can also register components globally. More here: https://v2.vuejs.org/v2/guide/components.html#Local-Registration
Are you showing us all of Card.vue? For a valid single-file vue component, I would expect to see <template>, <script> and <style> elements. The render function will be generated from whatever you put in the <template> element.
Make sure that the component that you want to reuse is wrapped inside a template tag
As follows
<template>
<div>
<component data/>
<div/>
<template/>
Then register it inside the parent
Like so
export default {
name: "Card",
components: {
Card
},
};