f / vue-wait

Complex Loader and Progress Management for Vue/Vuex and Nuxt Applications
MIT License
2k stars 101 forks source link

vuex-module-decorators Integration #90

Open No3x opened 4 years ago

No3x commented 4 years ago

Is there a way to integrate this with vuex-module-decorators? mapWaitingActions won't work here since the action ist mapped in by @Action.

Example taken from https://blog.logrocket.com/how-to-write-a-vue-js-app-completely-in-typescript/ but with modifications to make it work:

npm install vuex-module-decorators -D
npm install vuex-class -D
// store/modules/user.ts
import { VuexModule, Module, Mutation, Action } from 'vuex-module-decorators'
import store from "@/store";

interface IUserState {
  name: string
}

@Module({ store: store, namespaced: true })
export default class User extends VuexModule implements IUserState {
  public name: string = ''
  @Mutation
  public setName(newName: string): void {
    this.name = newName
  }

  get nameUpperCase() {
    return this.name.toUpperCase();
  }

  @Action
  public updateName(newName: string): void {
    this.context.commit('setName', newName)
  }
}
export default User
// store.ts
import Vue from 'vue'
import Vuex from 'vuex'
import User from '@/store/modules/user'
Vue.use(Vuex)
const store = new Vuex.Store({
  modules: {
    user: User
  }
})
export default store
<!-- User.vue -->
<template>
  <div class="details">
    <div class="username">User: {{ nameUpperCase }}</div>
    <input :value="name" @keyup="updateName($event.target.value)" />
  </div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import { namespace } from 'vuex-class'
const user = namespace('user')
@Component
export default class User extends Vue {
  @user.State
  public name!: string

  @user.Getter
  public nameUpperCase!: string

  @user.Action
  public updateName!: (newName: string) => void
}
</script>

And a variant of the similar @MutateAction:

// store/modules/user.ts
import {
  Module, Mutation, MutationAction,
  VuexModule, Action,
} from 'vuex-module-decorators';
import { Veterinarian, VeterinarianControllerApi, VeterinarianSpecialisationEnum } from '@/services';
import { Getter } from 'vuex-class';
import store from '@/store';

interface IUserState {
  name: string
}

@Module({ store, namespaced: true })
export default class User extends VuexModule implements IUserState {
  public name: string = 'defaultValue'

  get nameUpperCase() {
    return this.name.toUpperCase();
  }

  @MutationAction({ mutate: ['name'] })
  async updateName(newName: string) {
    return { name: newName };
  }
}

Wrapping the invocation of the Actions would be nice to trigger the wait start and end.

What I tried: I created my own WaitingMutationAction decorator. I just took the original one and wrapped the action with start and end:

WaitingMutationAction ```diff export interface MutationActionParams { mutate?: (keyof Partial)[] rawError?: boolean root?: boolean } -function mutationActionDecoratorFactory(params: MutationActionParams) { +function waitingMutationActionDecoratorFactory(params: MutationActionParams) { return function( target: T, key: string | symbol, descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise>> ) { const module = target.constructor as Mod const action: Act = async function( context: ActionContext, payload: Payload ) { try { + this._vm.$wait.start("test"); const actionPayload = await mutactFunction.call(context, payload) context.commit(key as string, actionPayload) } catch (e) { if (params.rawError) { throw e } else { console.error('Could not perform action ' + key.toString()) console.error(e) return Promise.reject(e) } + } finally { + this._vm.$wait.end("test"); } } const mutation: Mut = function( state: typeof target | Store, payload: Payload & { [k in keyof T]: any } } module.actions![key as string] = params.root ? { root: true, handler: action } : action module.mutations![key as string] = mutation } } -export function MutationAction( +export function WaitingMutationAction( target: { [k in keyof T]: T[k] | null }, key: string | symbol, descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise> ): void -export function MutationAction( +export function WaitingMutationAction( params: MutationActionParams ): ( target: T, key: string | symbol, descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise> ) => void * * @param paramsOrTarget the params or the target class * @param key the name of the function * @param descriptor the function body * @constructor */ -export function MutationAction( +export function WaitingMutationAction( paramsOrTarget: MutationActionParams | M, key?: string | symbol, descriptor?: TypedPropertyDescriptor<(...args: any[]) => Promise>> ): | (( target: T, @MutationAction({mutate: ['incrCount']}) async getCountDelta() { return {incrCount: 5} } * */ - return mutationActionDecoratorFactory(paramsOrTarget as MutationActionParams) + return waitingMutationActionDecoratorFactory(paramsOrTarget as MutationActionParams) } else { /* * This is the case when `paramsOrTarget` is target. * i.e. when used as - *
         @MutationAction
         async getCountDelta() {
           return {incrCount: 5}
         }
      * 
*/ - mutationActionDecoratorFactory({} as MutationActionParams)( + waitingMutationActionDecoratorFactory({} as MutationActionParams)( paramsOrTarget as K, key!, descriptor! ) } } ```

Well, it works. But it's too much code duplication and dumb.