Adding vue-property-decorator to vue-class-component
Some of the most important parts of Vue are missing in the vue-class-component in the form of TypeScript decorators. So, the community made a library called vue-property-decorator that is fully endorsed by the Vue core team.
This library brings some of the missing parts as ECMAScript proposal decorators, such as props, watch, model, inject, and so on.
Getting ready
The pre-requisite for this recipe is as follows:
- Node.js 12+
The Node.js global objects that are required are as follows:
- @vue/cli
- @vue/cli-service-global
How to do it...
First, we need to create our Vue CLI project. We can use the one we created in the last recipe or start a new one. To find how to create a Vue CLI project with TypeScript, please check the 'Creating a custom mixin with vue-class-component' recipe.
Follow these steps to add vue-property-decorator to a Vue class-based component:
- We need to add the vue-property-decorator to our project. Open Terminal (macOS or Linux) or Command Prompt/PowerShell (Windows) and execute the following command:
> npm install -S vue-property-decorator
- In the components mixin, we will add a decorator for receiving a prop, which will be a value for our number that is calculated:
import {
Vue,
Component,
Prop,
} from 'vue-property-decorator';
@Component
export default class DefaultNumber extends Vue {
valueNumber: number = 0;
@Prop(Number) readonly value: number | undefined;
get formattedNumber() {
return `Your total number is: ${this.valueNumber}`;
}
}
- With that number, we need to make the watchers emit the event to the parent component when the value changes, and update the value inside when the value is changed within the parent component. To do this, we need to create a new file called numberWatcher.ts inside the src/mixins folder:
import {
Watch,
Mixins,
} from 'vue-property-decorator';
import DefaultNumber from './defaultNumber';
export default class NumberWatchers extends Mixins(DefaultNumber) {
@Watch('valueNumber')
onValueNumberChanged(val: number) {
this.$emit('input', val);
}
@Watch('value', { immediate: true })
onValueChanged(val: number) {
this.valueNumber = val;
}
}
- With our mixin updated, our components need to be updated too. First, we will update the Counter.vue component, changing the imported mixin from the defaultNumber.ts file to numberWatcher.ts:
<template>
<div>
<fieldset>
<legend>{{ this.formattedNumber }}</legend>
<button @click="increase">Increase</button>
<button @click="decrease">Decrease</button>
</fieldset>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import Component, { mixins } from 'vue-class-component';
import NumberWatcher from '../mixins/numberWatcher';
@Component
export default class Counter extends mixins(NumberWatcher) {
increase() {
this.valueNumber += 1;
}
decrease() {
this.valueNumber -= 1;
}
}
</script>
- Now, we will update the CounterByTen.vue component, and add the newly created mixin:
<template>
<div>
<fieldset>
<legend>{{ this.formattedNumber }}</legend>
<button @click="increase">Increase By Ten</button>
<button @click="decrease">Decrease By Ten</button>
</fieldset>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import Component, { mixins } from 'vue-class-component';
import NumberWatcher from '../mixins/numberWatcher';
@Component
export default class CounterByTen extends mixins(NumberWatcher) {
increase() {
this.valueNumber += 10;
}
decrease() {
this.valueNumber -= 10;
}
}
</script>
- With everything settled, we just need to update the App.vue component. This time, we will store a variable in the component that will be passed down to both of the child components, and when the components emit the update events, this variable will change automatically, updating the other components too:
<template>
<div id="app">
<Counter
v-model="amount"
/>
<hr />
<CounterByTen
v-model="amount"
/>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import Counter from './components/Counter.vue';
import CounterByTen from './components/CounterByTen.vue';
@Component({
components: {
Counter,
CounterByTen,
},
})
export default class App extends Vue {
amount: number = 0;
}
</script>
<style lang="stylus">
#app
font-family 'Avenir', Helvetica, Arial, sans-serif
-webkit-font-smoothing antialiased
-moz-osx-font-smoothing grayscale
text-align center
color #2c3e50
margin-top 60px
</style>
How it works...
By injecting the decorators at the vue-class-components, the vue-property-decorator helps the TypeScript compiler check for the types and static analysis of your Vue code.
We used two of the decorators available, the @Watch and @Prop decorators.
As we took apart the common parts of our code in the form of mixins, the process implementation became easier.
The parent component passed down a property to the child component, passing the initial value and the subsequently updated value.
This value is checked and updated inside the child component, which is used to update a local variable used by the calculation functions. When the calculation is done and the value is changed, the watcher emits an event that is passed to the parent component, which updates the main variable, and the loop goes on.
There's more...
There is another library that works the same as the vue-property-decorator, but for the vuex plugin, called vuex-class.
This library uses the same process as vue-property-decorator. It creates an inject decorator in the component. Those decorators help the TypeScript compiler to check for types in the development process.
You can find more information about this library at https://github.com/ktsn/vuex-class/
See also
You can find more information about the vue-property-decorator at https://github.com/kaorun343/vue-property-decorator