I'm trying to write a unit test for a VueJS 2 component that has a dynamic component that changes as the state changes.
<template>
<div>
<component ref="dynamicComponent" id="dynamicComponent" :is="someDynamicType" #custom-event="handleCustomEvent">
</component>
</div>
</template>
What i'm trying to test is that the parent component is handling the custom-event from the child dynamic component correctly. However, the problem I'm running into is that I cannot seem to get a reference to the child dynamic component.
I've tried
await wrapper.findComponent('#dynamicComponent').vm.$emit("custom-event", {});
and
await wrapper.find({ref: 'dynamicComponent'}).vm.$emit("custom-event", {});
and I also tried using the known type of the dynamic component
await wrapper.findComponent(KnownSubType).vm.$emit("custom-event", {});
but in each case, I get the following error
TypeError: Cannot read properties of undefined (reading '$emit')
Is there another way to obtain the component so that I can emit the custom event or is there another way to trigger the event to test my parent component?
I ran into a similar problem writing tests using dynamic components. To ensure that the child component was loaded before manipulating it in my tests I had to flush promises. This can be either written manually like in this answer: https://stackoverflow.com/a/65665052/11015616
or
the flush-promises npm library can be used.
const wrapper = mount(SomeComponent, {options});
await flushPromises();
// manipulate child component
Related
I'm writing Vue2, and feel confused about life cycle of Vue.
Please help me, thx. :)
1.
If there is a prop called count.
Why can't count be accessed via this.count in beforeCreate, but it can be accessed via this.$options.propsData.count in same hook?
2.
what's the difference between this.count and this.$options.propsData.count?
3.
What's the meaning of "after props resolution" of sentence below?
Called immediately when the instance is initialized, after props resolution, before processing other options such as data() or computed
I did below,
<!-- child component in parent component -->
<Card :count="33" :data="data"></Card>
<!-- beforeCreate hook of child component -->
beforeCreate(): void {
console.log(this.$options);
}
then get something like this picture.
I'm converting some components from vue 3's option API to the composition API. In this particular component I have two nested child components:
<script lang="ts" setup>
import ShiftOperation from "#/components/transformation-widgets/ShiftOperation.vue";
import RawJolt from "#/components/transformation-widgets/RawJolt.vue";
console.log([ShiftOperation, RawJolt])
...
From what I understand, if you're using the setup attribute in the script tag then all you have to do is import the component into a variable like I'm doing above and it should be available for the template without having to do anything else, like it's not like the old options api where you had to inject those components into the parent component.
Both components are imported successfully (confirmed by the console log:
When I'm rendering out this parent component I'm using the two child components to render out an array of data where I reference the children dynamically in the template based on information in each block of data that I'm iterating over:
<template>
<div class="renderer-wrapper">
<component
v-for="(block, index) in store.specBlocks"
v-bind:key="index"
:block="block"
:index="index"
:is="determineBlockComponent(block)"
#block-operation-updated="updateBlock"
>
</component>
</div>
</template>
// logic for determining the component to use:
export const determineBlockComponent = (block: JoltOperation) => {
switch (block.renderComponent) {
case 'shift':
return 'ShiftOperation'
default:
return 'RawJolt'
}
}
This worked fine in the options api version of it, but for some reason the components don't actually render. They show up in the elements tab:
But they don't show up in the view. I also added a created lifecycle hook into the child components that just console.log's out saying "created X", but those hooks don't fire.
Business logic wise nothing has changed, it's just been going from option api to composition api, so I'm assuming I'm missing some key detail.
Any ideas?
Your determineBlockComponent function should not return the string but the object of the component. Replace return 'ShiftOperation' with return ShiftOperation
I'm using Vue3 with the composition API. In a form-component I put ref's on each field (child-component).
For some reason the ref's of the custom components are different from ref's for Quasar components.
When I console.log a ref to a custom component I get this in DevTools:
Proxy {__v_skip: true}
(without any properties in Target)
while a ref to a Quasar components gives this :
Proxy {…}
(with all properties of the component in Target)
For this reason I can't use the ref to access properties or methods of these child components.
I have no idea what __v_skip even means.
My custom components are defined with script setup, could that be a reason?
Any idea how to fix this?
UPDATE
If I use defineExpose in the child components for the properties and methods I want to access from outside with a ref, it does work. Not really handy though, since these components have lots of props.
Seem likes currently you cannot access the custom component by ref, if your component is written by Composition API (<script setup>). But you can try the way I mention underneath.
In the Vue 3 doc, there are some lines mentioned this behavior:
An exception here is that components using <script setup> are private
by default: a parent component referencing a child component using
<script setup> won't be able to access anything unless the child
component chooses to expose a public interface using the defineExpose
macro
Read more here: Vue 3 - Ref on Component
That means if you want to access anything from the custom component, your component has to expose that information. I think it's because in Vue 3 you don't need to have root component anymore, so if you define a ref, Vue does not know what the component you want to ref to.
But...
You can try to use yourRef.value.$el, maybe it will help.
Example:
// Parent.vue
<template>
<Child ref="childRef">
</template>
<script setup lang="ts">
// Import things...
const childRef = ref<InstanceType<typeof Child> | null>(null);
onMounted(() => {
console.log(childRef.value.$el);
});
</script>
I've been trying to pass data from child to parent. I'm not quite sure why what I've got right now isn't working. I should mention that the emitter gets fired but the method in the parent component doesn't seem to get called.
Children.vue:
<div #click="removeCommercials()" class="">
<u-button icon color="transparent">
<u-icon icon="switch-on" color="white"/>
</u-button>
</div>
methods: {
removeCommercials () {
this.$emit('test',{message:'HELLOOOO???'})
console.log("AAA")
}
},
Parent.vue:
<Children class="mt-4" #test="noCommercials"/>
methods: {
noCommercials(deactivate) {
console.log("?????")
this.deactivateCommercials = deactivate.message
console.log("REMOVEING COMMERCIALS")
console.log(this.deactivateCommercials)
},
},
From the console I can see the text AAA. From the vue developer tools I can also see the emitter getting fired with the correct data. But the method noCommercials in the parent component doesn't seem to trigger. Nothing from the method gets printed in the console.
From some other posts I've seen people talking about using this.$parent.$emit instead of this.$emit but that didn't do the trick for me. I've also seen others having the same problem but that's because they used the wrong child component.
I'm really confused as to why the noCommercials method does not trigger at all in the parent component. What am I doing wrong?
ADDITIONAL INFO:
I tried to trigger the same method from a different child component with $emit and it works. Now I just need to figure out why this particular child component is giving me issues.
i have three vue.js file. where the parent file (parent.vue) contains data of profile:[]
the data of profile is send to child file (child.vue) using props methods.
the child.vue also has it's own child file (grandchild.vue) that has been pass with the profile data by using props.
grandchildren will send a put request to API to change some data in profile.
my question is. how can i make sure the props will update on every change made in the profile data.
information : (parent.vue = main file, child.vue = drawer(from ant design), grandchild = popover)
i need the child.vue to update the profile data after the grandchild succeed send a put request to the API.
is there any way or reference link so i could make the props update after a put request from grandchild.vue i have tried watch method but the problem is the user need to close the drawer (child.vue) first and re open the drawer to update the props. is there any way the props update without closing the drawer?
example of code :
parent.vue :
// structure
<child.vue
:profile="profile"
/>
child.vue & grandchild.vue :
//script
props : [profile],
profile can be used as {{profile.subsdata}} in html or this.profile.subsdata javascript
Use an emit. Tell the parent component to update via another GET request or just pass the data back directly.
Child Method:
notifyParent () {
this.$emit('updateProfile')
}
Parent Template:
<ChildComponent v-on:updateProfile="someMethod"/>
Parent Method:
someMethod () {
//GET request or whatever
}
More details here: https://v2.vuejs.org/v2/guide/components-custom-events.html
The keyword here is eventBus, you need an eventBus to $emit an event changing the data in the parent component from the grandchild component. If you only need to change up the data 1 layer instead of 2 in this case, you only need custom event + $emit, without the eventBus. But as it's greater than 2 layers, you need eventBus, or even more relegent ways to do state management.