michaelolof / vuex-class-component

A Type Safe Vuex Module or Store Using ES6 Classes and ES7 Decorators written in TypeScript.
217 stars 21 forks source link

Example on how to use with Nuxt #89

Open SandroMaglione opened 4 years ago

SandroMaglione commented 4 years ago

I am trying to make the library work with Nuxt.

The docs say that what you need is:

export class UserStore extends createModule({ target: "nuxt" }) {
  ...
}

To test it, I created a new file in the store folder of my Nuxt project. The file contains the class of the example in the docs:

import { createModule, mutation, action } from 'vuex-class-component'

export class UserStore extends createModule({
  strict: false,
  target: 'nuxt'
}) {
  firstname = 'Michael'
  lastname = 'Olofinjana'
  specialty = 'JavaScript'

  @mutation clearName() {
    this.firstname = ''
    this.lastname = ''
  }

  @action async doSomethingAsync() {
    const val1 = await new Promise(() => this.firstname)
    return val1
  }

  @action async doAnotherAsyncStuff(payload: { func: Function }) {
    const number = await this.doSomethingAsync()
    payload.func()
    return payload + this.fullname + number
  }

  // Explicitly define a vuex getter using class getters.
  get fullname() {
    return this.lastname + this.firstname
  }

  // Define a mutation for the vuex getter.
  // NOTE this only works for getters.
  set fullname(name: string) {
    const names = name.split(' ')
    this.firstname = names[0]
    this.lastname = names[1]
  }

  get bio() {
    return `Name: Specialty: ${this.specialty}`
  }
}

I would expect this code to create a new module with the name of the file and relative state (firstname, lastname, specialty), and the getters defined by the class, as well as actions and mutations.

The code compiles, but then the store seems to be empty (from Vuejs devtool): user: Object (empty), and getter are not present at all.

How can I make it work? Am I missing something?

swetjen commented 3 years ago

I think the problem with your code above is here:

export class UserStore extends createModule({ strict: false, target: 'nuxt' }) { ...

Instead it should be like this:

const VuexModule = createModule({
  namespaced: "main",
  strict: false,
  target: "nuxt",
})

export class MainStore extends VuexModule {
   ...

You need to namespace the store.

michael-scheurer commented 2 years ago

See for a fully working example: https://github.com/TAC/nuxt-vuex-typescript-example/blob/master/app/src/store/modules/counter.ts

moifort commented 2 years ago

Little update, there is a new version of the library. This is my solution, feel free to comment :)

store/user.ts

import { action, createModule, getter, mutation } from 'vuex-class-component'
import { extractVuexModule } from 'vuex-class-component/dist/module'

export class User extends createModule({
  target: 'nuxt',
  enableLocalWatchers: true,
}) {
  private firstname = 'Michael'
  private lastname = 'Olofinjana'
  @getter speciality = 'JavaScript' // The @getter decorator automatically exposes a defined state as a getter.
  @getter occupation = 'Developer'

  static $watch = {
    speciality: (newValue: string) =>
      console.log(`Fullname has changed ${newValue}`),
  }

  static $subscribeAction = {
    doSomethingAsync() {
      console.log(`fetchDetails action was called with payload`)
    },
  }

  @mutation changeName({
    firstname,
    lastname,
  }: {
    firstname: string
    lastname: string
  }) {
    this.firstname = firstname
    this.lastname = lastname
  }

  @action doSomethingAsync() {
    this.speciality = this.$store.app.$vxm.user.firstname
    return Promise.resolve('20')
  }

  @action async doAnotherAsyncStuff(payload: string) {
    const number = await this.doSomethingAsync()
    console.log(number)
    this.changeName({ firstname: 'John', lastname: 'Doe' })
    return payload + this.fullName
  }

  // Explicitly define a vuex getter using class getters.
  get fullName() {
    return this.firstname + ' ' + this.lastname
  }
}

export default Object.values(extractVuexModule(User))[0]

Vue Chrome plugin

Capture d’écran 2022-05-02 à 11 29 41

Accessor Plugin plugins/store.ts

import { Plugin } from '@nuxt/types'
import { createProxy } from 'vuex-class-component'
import { User } from '~/store/user'
import { Car } from '~/store/car'

const createVxm = (store: any) => ({
  user: createProxy(store, User),
  car: createProxy(store, Car),
})

type vxmType = ReturnType<typeof createVxm>

declare module 'vue/types/vue' {
  interface Vue {
    $vxm: vxmType
  }
}

declare module '@nuxt/types' {
  interface NuxtAppOptions {
    $vxm: vxmType
  }
  interface Context {
    $vxm: vxmType
  }
}

const storePlugin: Plugin = ({ store }, inject) => {
  inject('vxm', createVxm(store))
}

export default storePlugin