Vue 3 as a class component - vue.js

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>

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

Defining types of Vue 2.x components

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 }

Vue import component within functional component

I have a component called SpotifyButton in the components directory that looks like this:
<template functional>
<b-button pill size="sm" :href="props.spotifyUri" class="spotify-green">
<b-img-lazy
src="~/assets/Spotify_Icon_RGB_White.png"
height="20"
width="20"
/>
View on Spotify
</b-button>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
name: 'SpotifyButton',
props: {
spotifyUri: {
type: String,
required: true
}
}
});
</script>
I'm able to import and use this in a component in the pages directory like so without any problem:
<template>
<spotify-button :spotify-uri="artist.uri"/>
</template>
<script lang="ts">
import Vue from 'vue';
import { Context } from '#nuxt/types';
import FullArtist from '#/types/FullArtist';
import SpotifyButton from '#/components/SpotifyButton.vue';
export default Vue.extend({
name: 'ArtistPage',
components: {
SpotifyButton
},
async asyncData({ $axios, params, error }: Context) {
try {
const artist: FullArtist = await $axios.$get(`/api/artists/${params.id}`);
return { artist };
} catch (e) {
error({ statusCode: 404, message: 'Artist not found' });
}
},
data() {
return {
artist: {
name: ''
} as FullArtist
};
}
});
</script>
However if I try to import SpotifyButton into another component in the components directory in the same way, I get the following error.
Here is the ArtistPreview component, which is in the components directory:
<template functional>
<spotify-button :spotify-uri="props.artist.uri"/>
</template>
<script lang="ts">
import Vue, { PropType } from 'vue';
import SpotifyButton from '#/components/SpotifyButton.vue';
import SimpleArtist from '#/types/SimpleArtist';
export default Vue.extend({
name: 'ArtistPreview',
components: {
SpotifyButton
},
props: {
artist: {
type: Object as PropType<SimpleArtist>,
required: true
}
}
});
</script>
Am I missing something? Why does an import that works perfectly fine in a pages directory component not work in a components directory component?
This was happening because I'm using functional components. It turns out you can't nest functional components without doing some funky workarounds. Here's the GitHub issue with a few solutions.
I went with the first solution, so my ArtistPreview component now looks something like this:
<template functional>
<spotify-button :spotify-uri="props.artist.uri"/>
</template>
<script lang="ts">
import Vue, { PropType } from 'vue';
import SpotifyButton from '#/components/SpotifyButton.vue';
import SimpleArtist from '#/types/SimpleArtist';
Vue.component("spotify-button", SpotifyButton);
export default Vue.extend({
name: 'ArtistPreview',
props: {
artist: {
type: Object as PropType<SimpleArtist>,
required: true
}
}
});
</script>
Go with:
import SpotifyButton from '~/components/SpotifyButton.vue'
With Typescript is better use another approach: Add 'nuxt-property-decorator' and follow his flow.
So, you define your component as follow:
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
import SpotifyButton from '~/components/SpotifyButton.vue'
#Component({
components: {
SpotifyButton
},
})
class AnotherComponent extends Vue {
...
}
export default AnotherComponent
</script>
[Nuxt Property Decorator on Github][1]
I think is important to read the official [Nuxt Typescript documentation][2] to a proper setup.
I hope it helps!
[1]: https://github.com/nuxt-community/nuxt-property-decorator
[2]: https://typescript.nuxtjs.org/

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.