Defining types of Vue 2.x components - vue.js

I use Vue 2.6 in my project
I want to define props of component for specific cases (for dynamic component in child component with passed props)
Here is some sample code
// child component
<template>
<component :is="someProp.passedComponent" />
</template>
import { Component, Vue, Prop } from 'vue-property-decorator'
#Component({
name: 'SomeComponent'
})
export default class SomeComponent extends Vue {
#Prop() readonly someProp!: { passedComponent: Vue }
}
// parent component
<template>
<some-component :some-prop="someDataForProp">
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import DummyComponent from './components/DummyComponent.vue'
#Component({
name: 'ParentComponent'
})
export default class ParentComponent extends Vue {
private someDataForProp = { passedComponent: DummyComponent }
}
</script>
// DummyComponent.vue
<template>
<div>{{ title }}</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
#Component({
name: 'DummyComponent'
})
export default class DummyComponent extends Vue {
private title = 'This is dummy component for example'
}
</script>
But when I define Component type as Vue, It makes some error like this
Type 'typeof DummyComponent' is missing the following properties from type 'Vue': $el, $options, $parent, $root, and 39 more.
I wonder if there is one type that can encompass all components defined with #Component.

passedComponent is expected to be a component itself that is used <component>, while Vue type refers to Vue component instance, i.e. component ref.
It should be:
#Prop() readonly someProp!: { passedComponent: typeof Vue }

Related

is it available to call the methods where in the vue component from the plugin?

I wanted to access the vue.data or methods in the plugin.
no matter what I tried several times, it didn't work.
such as eventBus, Mixin etc...
so I'm curious about the possibility to call the methods like that.
thank you for reading this question.
here is the custom component.
<template>
<div>
<v-overlay :value="isProcessing">
<v-progress-circular indeterminate size="64"></v-progress-circular>
</v-overlay>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
#Component
export default class ProgressCircular extends Vue {
private isProcessing: boolean;
startProcess() {
this.isProcessing = true;
}
}
</script>
and this is the plugin source.
import ProgressCircular from '#/components/ProgressCircular.vue';
import { VueConstructor } from 'vue';
import Vuetify from 'vuetify/lib';
import vuetify from './vuetify';
export default {
install(Vue: VueConstructor, options: any = {}) {
Vue.use(Vuetify);
options.vuetify = vuetify;
Vue.component('progress-circular', ProgressCircular);
Vue.prototype.$fireProgressing = function () {
// it didn't work
// I just wanted to access the method where in the Vue Component
// ProgressCircular.startProcess();
};
},
};
use the plugin syntax to extend vue like:
Vue.use({
install: Vue => {
Vue.prototype.$fireProgressing = () => {
};
}
});
or
Vue.use(YOURPLUGIN);
before you mount vue

Vue 3 as a class component

currently in the project we uses Vue 2.x and our components works in such way
#Component({
template: `
<div>
some code ....
<div> `
})
export default class class1 extends Vue {
#Prop() data: IsomeData;
}
vue-class-component and vue-property-decorator allows us to right in this way, according the docs, #Component was replaced to #Options({}).
How can I migrate to Vue3 without headbreaking refactoring?
Try this.
<template>
<div>
some code ....
<div>
</template>
<script>
import { Vue } from "vue-class-component";
import { Prop } from "vue-property-decorator";
export default class Home extends Vue {
#Prop() data: IsomeData;
}
</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 global component not defined

I am creating a Vue plugin that adds a custom component to the global scope like so:
import CustomComponent from './CustomComponent.vue';
const MyPlugin = {
install(Vue, options) {
Vue.component('CustomComponent', CustomComponent);
}
}
and the component itself is simply:
<template>
<h1>Hi from the custom component</h1>
</template>
<script>
export default {
name: 'CustomComponent',
mounted() {
console.log('hello console');
}
}
</script>
And then finally I import the plugin into my main.js file (I'm using Gridsome):
export default function (Vue, { router, head, isClient }) {
Vue.use(MyPlugin);
}
But now I expect that when I make a component I can extend the CustomComponent since it's in the global scope like so:
<template>
<h2>Hi again</h2>
</template>
<script>
export default {
name: 'RegularComponent',
extends: CustomComponent
}
</script>
But this gives me an error of Custom Component not defined and it seems like it's because CustomComponent isn't in the global scope because if I import the CustomComponent vue file in my RegularComponent vue file it works. However, this is not the behavior I would like and I cannot figure out why CustomComponent is not just globally available.
CustomComponent is a javascript object, you still need to import to use it
<script>
import CustomComponent from './CustomComponent.vue'
export default {
name: 'RegularComponent',
extends: CustomComponent
}
</script>
I think when define component as global, that means you can use it in template without re-declare:
<template>
<h2>Hi again</h2>
<custom-component />
</template>

How to test this component?

I have a Page level component which implements a component BookingInformation with slots. In the Page component, it's got another component BookingInformationHeader with slots. header and default.
My question is, how should I set up my test so that I can test that the GoogleConversionTrackingImage is visible when #Reservation.State wasBookingJustMade changes to true?
<script lang="ts">
import { Vue, Component } from "vue-property-decorator";
import { Reservation } from "#/store/vuex-decorators";
import { BookingInformation, BookingInformationHeader } from "#/components";
import GoogleConversionTrackingImage from './components/GoogleConversionTrackingImage.vue';
#Component({
components: {
BookingInformation,
BookingInformationHeader,
GoogleConversionTrackingImage
}
})
export default class ConfirmationPage extends Vue {
renderTrackingImage: boolean = false;
#Reservation.State wasBookingJustMade: boolean;
}
</script>
<template>
<booking-information page-type="confirmation-page" class="confirmation-page">
<template slot="header" slot-scope="bookingInfo">
<booking-information-header>
<template slot="buttons">
// some buttons
</template>
</booking-information-header>
<google-conversion-tracking-image v-if="wasBookingJustMade" />
</template>
</booking-information>
</template>
By using vue test utils https://vue-test-utils.vuejs.org/ and chai https://www.chaijs.com/ in your test file you can do something like:
import mount from "#vue/test-utils";
import expect from "chai";
const wrapper = mount(BookingInformation,<inner components you want to test>);
expect(googleImage.exists()).to.be.true;
wrapper.setData({
wasBookingJustMade: true,
});
const googleImage = wrapper.find("google-conversion-tracking-image");
expect(googleImage.exists()).to.be.false;
You'll probably need to import the page level component as well.
You can give an id to the component you want to find and then search by id.