Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nuxt support #2

Open
sobolevn opened this issue Jun 23, 2019 · 7 comments
Open

Nuxt support #2

sobolevn opened this issue Jun 23, 2019 · 7 comments

Comments

@sobolevn
Copy link

  • I'm submitting a ...
    [x] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [x] question about how to use this project

I have this page component in nuxt:

@Component({})
export default class Index extends mixins(TypedStoreMixin) {
  fetch ({ store }: { store }): Promise<RawCommentType[]> {
    // Here we don't have a DI setup yet, so we use the explicit approach:
    const typedStore = useStore<TypedStore>(store)
    return typedStore.comments.fetchComments()
  }

  fetchComments () {
    this.typedStore.comments.fetchComments()
  }
}

When I use fetch method from Nuxt I see this error:

Cannot determine a class of the requesting service "function TestService() {}"

Снимок экрана 2019-06-23 в 15 29 50

But, if fetch is not used and I instead use fetchComments method on button click - it works fine.

I guess the reason is that nuxt handles fetch differently and something is not setup properly.

@sobolevn
Copy link
Author

Here's my module definition:

import 'reflect-metadata'
import { Action, Mutation, State, Getter } from 'vuex-simple'
import { Container, Service } from 'vue-typedi'

import { CommentType, CommentPayloadType } from '~/logic/comments/types'
import { RawCommentType } from '~/logic/comments/models'
import CommentService from '~/logic/comments/services/api'

@Service()
class TestService {
  public vas (): number {
    return 1
  }
}

export default class CommentsModule {
  // State

  @State()
  public comments: CommentType[] = []

  // Getters

  @Getter()
  public get hasComments (): boolean {
    return Boolean(this.comments && this.comments.length > 0)
  }

  // Mutations

  @Mutation()
  public setComments (payload: RawCommentType[]): void {
    const updatedComments: CommentType[] = []

    for (const comment of payload.slice(0, 10)) {
      // We transform RawCommentType in CommentType here:
      updatedComments.push({ ...comment, 'rating': 0 })
    }

    this.comments = updatedComments
  }

  @Mutation()
  public updateRating ({ commentId, delta }: CommentPayloadType): void {
    if (!this.comments) return

    const commentIndex = this.comments.findIndex((comment): boolean => {
      return comment.id === commentId
    })

    if (!this.comments || !this.comments[commentIndex]) return

    this.comments[commentIndex].rating += delta
  }

  // Actions

  @Action()
  public async fetchComments (): Promise<RawCommentType[]> {
    console.log(Container.get(TestService), Container.get(TestService).vas())
    const service = Container.get(CommentService)
    const commentsList = await service.fetchComments()
    this.setComments(commentsList)
    return commentsList
  }
}

My typed store:

import { Module } from 'vuex-simple'

// TODO: document

import CommentsModule from '~/logic/comments/module'

export default class TypedStore {
  @Module()
  public comments = new CommentsModule()
}

And store from nuxt:

// We use default Nuxt Module-based store,
// read more about it here:
// https://nuxtjs.org/guide/vuex-store

// TODO: document

import Vue from 'vue'
import Vuex from 'vuex'
import { createVuexStore } from 'vuex-simple'
import { Container } from 'vue-typedi'

import TypedStore from '~/logic/store'
import tokens from '~/logic/tokens'

Vue.use(Vuex)

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export default function store () {
  const typedStore = new TypedStore()

  // Registering DI container items:
  Container.set(tokens.STORE, typedStore)
  Container.set(tokens.COMMENTS, typedStore.comments)

  return createVuexStore(typedStore, {
    'strict': false,
    'modules': {},
    'plugins': [],
  })
}

@sobolevn
Copy link
Author

Here's the fix for this case: typestack/typedi#99

@sobolevn
Copy link
Author

The next problem is that Reflect.getMetadata returns undefined here: https://github.com/typestack/typedi/blob/ba2d73f245ec92f8d67538e2409ec55d2c5cdb7d/src/decorators/Inject.ts#L42

For some reason Reflect.getMetadata("design:type", target, propertyName) does not work.
Maybe metadata is not transferred?

@sobolevn
Copy link
Author

This is what I get:
Снимок экрана 2019-06-23 в 19 35 22

@sobolevn
Copy link
Author

Code:

import CommentService from '~/logic/comments/services/api'

@Injectable()
export default class CommentsModule {
  // Dependencies

  @Inject()
  public service!: CommentService
  // ...
}

@sobolevn
Copy link
Author

The thing is that plugins are called after new store is created in nuxt:

import 'reflect-metadata'
import { AxiosInstance } from 'axios'
import Vue, { VueConstructor } from 'vue'
import VueTypeDI, { Container } from 'vue-typedi'

import tokens from '~/logic/tokens'

export function install (
  vueConstructor: VueConstructor,
  $axios: AxiosInstance,
): void {
  vueConstructor.use(VueTypeDI)
  Container.remove(tokens.AXIOS)
  Container.set(tokens.AXIOS, $axios)
}

/* istanbul ignore next */
export default ({ $axios }): void => {
  install(Vue, $axios)
}

@sobolevn
Copy link
Author

sobolevn commented Jun 23, 2019

And store.ts:

// We use default Nuxt Module-based store,
// read more about it here:
// https://nuxtjs.org/guide/vuex-store

// TODO: document

import Vue from 'vue'
import Vuex from 'vuex'
import { createVuexStore } from 'vuex-simple'
import { Container } from 'vue-typedi'

import tokens from '~/logic/tokens'
import TypedStore from '~/logic/store'

Vue.use(Vuex)

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export default function store () {
  if (!Container.has(tokens.AXIOS)) {
    Container.set(tokens.AXIOS, (noop) => {})
  }

  const typedStore = new TypedStore()

  // Registering DI container items:
  Container.set(tokens.STORE, typedStore)

  return createVuexStore(typedStore, {
    'strict': false,
    'modules': {},
    'plugins': [],
  })
}

Service:

import { AxiosInstance } from 'axios'
import { Service, Container } from 'vue-typedi'
import * as ts from 'io-ts'
import * as tPromise from 'io-ts-promise'

import tokens from '~/logic/tokens'
import { RawComment, RawCommentType } from '~/logic/comments/models'

@Service(tokens.COMMENT_SERVICE)
export default class CommentService {
  protected get $axios (): AxiosInstance {
    return Container.get(tokens.AXIOS) as AxiosInstance
  }

  /**
   * Fetches comments from the remote API.
   *
   * @returns Parsed response data.
   */
  public async fetchComments (): Promise<RawCommentType[]> {
    // Note, that $axios has some custom methods, that are not used on purpose
    // https://github.com/nuxt-community/axios-module#-features
    const response = await this.$axios.get('comments')
    return tPromise.decode(ts.array(RawComment), response.data)
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant