vue-wait icon indicating copy to clipboard operation
vue-wait copied to clipboard

vuex-module-decorators Integration

Open No3x opened this issue 4 years ago • 0 comments

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
 export interface MutationActionParams<M> {
   mutate?: (keyof Partial<M>)[]
   rawError?: boolean
   root?: boolean
 }
 
-function mutationActionDecoratorFactory<T extends Object>(params: MutationActionParams<T>) {
+function waitingMutationActionDecoratorFactory<T extends Object>(params: MutationActionParams<T>) {
   return function(
     target: T,
     key: string | symbol,
     descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<Partial<T>>>
   ) {
     const module = target.constructor as Mod<T, any>
 
     const action: Act<typeof target, any> = async function(
       context: ActionContext<typeof target, any>,
       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<typeof target> = function(
       state: typeof target | Store<T>,
       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<K, T extends K>(
+export function WaitingMutationAction<K, T extends K>(
   target: { [k in keyof T]: T[k] | null },
   key: string | symbol,
   descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<K>>
 ): void
 
-export function MutationAction<T>(
+export function WaitingMutationAction<T>(
   params: MutationActionParams<T>
 ): (
   target: T,
   key: string | symbol,
   descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<T>>
 ) => 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<T, K, M extends K>(
+export function WaitingMutationAction<T, K, M extends K>(
   paramsOrTarget: MutationActionParams<T> | M,
   key?: string | symbol,
   descriptor?: TypedPropertyDescriptor<(...args: any[]) => Promise<Partial<K>>>
 ):
   | ((
       target: T,
         @MutationAction({mutate: ['incrCount']})
         async getCountDelta() {
           return {incrCount: 5}
         }
      * </pre>
      */
-    return mutationActionDecoratorFactory(paramsOrTarget as MutationActionParams<T>)
+    return waitingMutationActionDecoratorFactory(paramsOrTarget as MutationActionParams<T>)
   } else {
     /*
      * This is the case when `paramsOrTarget` is target.
      * i.e. when used as -
      * <pre>
         @MutationAction
         async getCountDelta() {
           return {incrCount: 5}
         }
      * </pre>
      */
-    mutationActionDecoratorFactory({} as MutationActionParams<K>)(
+    waitingMutationActionDecoratorFactory({} as MutationActionParams<K>)(
       paramsOrTarget as K,
       key!,
       descriptor!
     )
   }
 }
Well, it works. But it's too much code duplication and dumb.

No3x avatar Apr 11 '20 20:04 No3x