Vuex store as a decorated typescript class
- Download "vuex-typescript-decorator.ts" and place it in your project's /vuexts folder. I might turn it into an NPM package, when I learn how to do it...
- Create the store in /models/root.ts
import Vue from "vue";
import * as Vuex from "vuex";
import {store, action, mutation, asVuex} from '/vuexts/vuex-typescript-decorator';
@store
class TAppStore {
protected _counter: number=0; //converted to state
get counter() { //converted to getter
return this._counter;
}
@mutation protected SET_COUNTER(payload: {value?: number}) { //converted to mutation
if (payload.value===undefined)
this._counter++;
else
this._counter=payload.value;
}
@action async setCounter(payload: {value?: number}): Promise<void> //converted to action
{
this.SET_COUNTER(payload);
}
}
Vue.use(Vuex);
export { TAppStore };
export var AppStore=new TAppStore();
export var AppStoreVuexTS=asVuex(AppStore);
- Initialize Vue in /index.ts
import Vue from 'vue';
import App from '/App.vue';
import { AppStoreVuexTS } from "/models/root";
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>',
store: AppStoreVuexTS
})
- In your component, define a helper function to convert this.$store into the typed store. Or, you can access AppStore directly if you only have 1 instance.
// SomePage.vue
...
<script lang="ts">
...
import { TAppStore } from "/models/root";
import { getStore } from "/vuexts/vuex-typescript-decorator";
@Component()
export default SomePage extends Vue {
protected get store(): TAppStore
{
let res=getStore<TAppStore>(this.$store);
return res;
}
- And finally, use it:
// SomePage.vue
<template>
...
{{counter}}
<button @click="setCounter()">Increment</button>
<button @click="setCounter(0)">Zero</button>
...
</template>
<script lang="ts">
get counter() {
return this.store.counter;
}
setCounter(value?: number) {
this.store.setCounter({value: value});
}
</script>
(to be improved)
var AppStoreOptions: Vuex.StoreOptions<TAppStore>={
modules: {
stations: {
namespaced: true
}
},
plugins: [function (x: Vuex.Store<TAppStore>): string {
return "Hello";
}],
strict: true
};
@store(AppStoreOptions)
class TAppStore {
...
@store({strict: true})
class TAppStore {
...
import { store, hidden } from "/vuexts/vuex-typescript-decorator";
@store TAppStore {
@hidden protected _localStorageDirty: boolean=true; //can be assigned outside mutations
@hidden get localStorageDirty() {return this._localStorageDirty;} //not converted to Vuex getter
set localStorageDirty(lsd: boolean) {this._localStorageDirty=lsd;}
Modules need to be decorated with @module instead of @store. Modules should always be namespaced, the code is not tested or developed for namespaced: false.
@module({namespaced: true})
export class StationData
{
protected _identifier: string;
get identifier() {return _identifier;}
If you're adding the module at 2nd or lower level, you need to create the path in the store. I also recommend making a placeholder for the module(s).
@store({
modules: { //create path of modules where we will be adding at runtime
stations: {namespaced: true}
}
})
class TAppStore {
//create placeholder so we can access the added modules from store,
//rather than keeping the list of modules somewhere else
//This is just a Typescript placeholder. The object will be managed by Vuex.
stations: { [module_name: string]: StationData };
You have to register modules at runtime, using functions registerModule and unregisterModule.
import {registerModule as registerVuexModule,
unregisterModule as unregisterVuexModule} from "/vuexts/vuex-typescript-decorator";
//add a new module to @store.stations.[identifier]
let that: TAppStore;
let module: StationData;
registerVuexModule<StationData>(that, ['stations', module.identifier], module);
//unregister
unregisterVuexModule<StationData>(that, ['stations', module.identifier]);
// SomeStationPage.vue
import { getModule } from "/vuexts/vuex-typescript-decorator";
@Component() SomeStationPage extends Vue {
protected get store(): StationData|undefined
{
let module=this.$store.state.stations[this.$route.params.identifier];
let res: StationData|undefined=undefined;
if (module)
res=getModule<StationData>(module);
return res;
}
Both expression and function variants are supported on decorators. No options are defined for decorators other than @store and @module.
@action async setCounter(payload: {value?: number}): Promise<void>
@action() async setCounter(payload: {value?: number}): Promise<void>