vue-typedi
vue-typedi copied to clipboard
Nuxt support
- 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() {}"

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.
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': [],
})
}
Here's the fix for this case: https://github.com/typestack/typedi/issues/99
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?
This is what I get:
Code:
import CommentService from '~/logic/comments/services/api'
@Injectable()
export default class CommentsModule {
// Dependencies
@Inject()
public service!: CommentService
// ...
}
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)
}
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)
}
}