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;
}
}
In Vue, the v-model directive works like a sugar syntax, as a combination of the Vue $emit function and the Vue props function. When the value is changed, the component needs to $emit with the 'input' name, and the component needs to have in the props function a value key, which will be the value that will be passed down from the parent component to the child component.
- 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>