Skip to content

Commit

Permalink
Fixed some imports to avoid circular references, and improved the way…
Browse files Browse the repository at this point in the history
… we export and consume the Vuex store to be able to use strongly-typed code and avoid the ugly string interpolation for moduleName/mutationName throughout the code (see Chapter 9 as it has been updated as well in the book)
  • Loading branch information
Damiano committed Sep 26, 2020
1 parent 2a0f145 commit f30bad3
Show file tree
Hide file tree
Showing 18 changed files with 134 additions and 48 deletions.
11 changes: 6 additions & 5 deletions composition-api/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
<script lang="ts">
import { defineComponent, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { MutationType, StoreModuleNames } from '@/models/store'
import { store } from '@/store'
import { MutationType } from '@/models/store'
import { useLocalesStore } from '@/store'
import { config } from '@/config'
import { LocaleInfoInterface } from '@/models/localization/LocaleInfo.interface'
import LocaleSelector from '@/components/locale-selector/LocaleSelector.component.vue'
Expand All @@ -30,16 +30,17 @@
},
setup() {
const i18n = useI18n()
const localesStore = useLocalesStore()
const availableLocales = computed(() => {
return store.state.localesState.availableLocales
return localesStore.state.availableLocales
})
const availableThemes = config.themes
const onLocaleClicked = (localeInfo: LocaleInfoInterface) => {
store.dispatch(
`${StoreModuleNames.localesState}/${MutationType.locales.selectLocale}`,
localesStore.action(
MutationType.locales.selectLocale,
localeInfo.locale
)
}
Expand Down
4 changes: 2 additions & 2 deletions composition-api/src/api-client/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ApiClientInterface } from '@/models/api-client/ApiClient.interface'
import apiMockClient from '@/api-client/mock'
import apiLiveClient from '@/api-client/live'
import apiMockClient from './mock'
import apiLiveClient from './live'
import { config } from '@/config'

// return either the live or the mock client
Expand Down
2 changes: 1 addition & 1 deletion composition-api/src/api-client/live/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiClientInterface } from '@/models/api-client/ApiClient.interface'
import itemsApiClient from '@/api-client/live/items'
import itemsApiClient from './items'

// create an instance of our main ApiClient that wraps the live child clients
const apiLiveClient: ApiClientInterface = {
Expand Down
2 changes: 1 addition & 1 deletion composition-api/src/api-client/mock/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiClientInterface } from '@/models/api-client/ApiClient.interface'
import itemsApiClient from '@/api-client/mock/items'
import itemsApiClient from './items'

// create an instance of our main ApiClient that wraps the mock child clients
const apiMockClient: ApiClientInterface = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<script lang="ts">
import { defineComponent, PropType } from 'vue'
import { ItemInterface } from '@/models/items/Item.interface'
import ItemComponent from '@/components/items/children/Item.component.vue'
import ItemComponent from './children/Item.component.vue'
import Loader from '@/components/shared/Loader.component.vue'
import { useI18n } from 'vue-i18n'
Expand Down
6 changes: 3 additions & 3 deletions composition-api/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { store } from './store'
import { i18n } from '@/plugins/vue-i18n-next-plugin'
import { MyAppScss } from '@/plugins/myapp-scss'
import { FlagIconsScss } from '@/plugins/flags-icons/'
import { i18n } from './plugins/vue-i18n-next-plugin'
import { MyAppScss } from './plugins/myapp-scss'
import { FlagIconsScss } from './plugins/flags-icons/'

createApp(App)
.use(store)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// GEN-IMPORTS
import { ItemsApiClientInterface } from './api-client/items'
import { ItemsApiClientInterface } from './items'

/**
* @Name ApiClientInterface
Expand Down
14 changes: 7 additions & 7 deletions composition-api/src/models/http-client/HttpClient.model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import axios, { AxiosRequestConfig, AxiosError, AxiosResponse } from 'axios'
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
import { HttpRequestParamsInterface } from './HttpRequestParams.interface'
import { HttpClientInterface } from './HttpClient.interface'
import { config } from '@/config'
Expand Down Expand Up @@ -37,12 +37,12 @@ export class HttpClientModel implements HttpClientInterface {

axios
.get(url, options)
.then((response: any) => {
.then((response: AxiosResponse) => {
resolve(response.data as T)
})
.catch((response: any) => {
.catch((error: AxiosResponse) => {
console.info('------ rejecting ----')
reject(response)
reject(error)
})
})
}
Expand All @@ -63,11 +63,11 @@ export class HttpClientModel implements HttpClientInterface {

axios
.post(url, payload, options)
.then((response: any) => {
.then((response: AxiosResponse) => {
resolve(response.data as T)
})
.catch((response: any) => {
reject(response)
.catch((error: AxiosResponse) => {
reject(error)
})
})
}
Expand Down
2 changes: 1 addition & 1 deletion composition-api/src/models/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export * from './root/RootState.interface'
export * from './root/RootStore.interface'
// export the RootStore model
export * from './root/RootStore.model'
// GEN-EXPORTS
// as you add more state modules, add additional exports for those here as well
// GEN-EXPORTS
export * from './items/ItemsState.interface'
export * from './locales/LocalesState.interface'
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// group our constants in a namespace:
export namespace StoreModuleNames {
// GEN-NAMESPACE-CONSTS
export const itemsState: string = 'itemsState'
export const localesState: string = 'localesState'
// as you add more state modules, add additional properties here following the same convention
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// group our constants in a namespace
// GEN-IMPORTS
import { ItemsMutationType } from '../items/ItemsMutationType'
import { LocalesMutationType } from '../locales/LocalesMutationType'

export namespace MutationType {
// GEN-NAMESPACE-CONSTS
export const items = ItemsMutationType
export const locales = LocalesMutationType
// as you add more domain-specific mutation types, add them here following the same convention
Expand Down
11 changes: 3 additions & 8 deletions composition-api/src/models/store/root/RootStore.interface.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import {
// GEN-IMPORTS
ItemsStateInterface,
LocalesStateInterface
} from '@/models/store'

//import { RootStateInterface } from './RootState.interface'
// GEN-IMPORTS
import { ItemsStateInterface } from '../items/ItemsState.interface'
import { LocalesStateInterface } from '../locales/LocalesState.interface'

/**
* @name RootStoreInterface
* @description
* Wraps together each store module interface in one place
*/
export interface RootStoreInterface {
//extends RootStateInterface {
// GEN-INTERFACE-PROPS
itemsState: ItemsStateInterface
localesState: LocalesStateInterface
Expand Down
20 changes: 20 additions & 0 deletions composition-api/src/store/items/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import { initialItemsState } from './initialState'
import { ItemInterface } from '@/models/items/Item.interface'
import apiClient from '@/api-client'

/**
* @name mutations
* @description
* Vuex Items mutations
*/
export const mutations: MutationTree<ItemsStateInterface> = {
loadingItems(state: ItemsStateInterface) {
state.loading = true
Expand All @@ -30,6 +35,11 @@ export const mutations: MutationTree<ItemsStateInterface> = {
}
}

/**
* @name actions
* @description
* Vuex Items actions
*/
export const actions: ActionTree<ItemsStateInterface, RootStateInterface> = {
loadItems({ commit }) {
commit(MutationType.items.loadingItems)
Expand All @@ -54,12 +64,22 @@ export const actions: ActionTree<ItemsStateInterface, RootStateInterface> = {
}
}

/**
* @name getters
* @description
* Vuex Items getters
*/
export const getters: GetterTree<ItemsStateInterface, RootStateInterface> = {}

// create our Items store instance
const namespaced: boolean = true
const state: ItemsStateInterface = initialItemsState

/**
* @name itemsState
* @description
* Vuex Items store
*/
export const itemsState: Module<ItemsStateInterface, RootStateInterface> = {
namespaced,
state,
Expand Down
14 changes: 10 additions & 4 deletions composition-api/src/store/locales/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Module, MutationTree, ActionTree, GetterTree } from 'vuex'

import { MutationType, RootStateInterface, LocalesStateInterface } from '@/models/store'

import { initialLocalesState } from './initialState'

import { LocaleInfoInterface } from '@/models/localization/LocaleInfo.interface'
import { i18n } from '@/plugins/vue-i18n-next-plugin'
import { LocalStorageKeys } from '@/models/local-storage/LocalStorageKeys'

/**
* @name mutations
* @description
* Vuex Locales mutations
*/
export const mutations: MutationTree<LocalesStateInterface> = {
selectLocale(state: LocalesStateInterface, localeId: string) {
// set only the model selected to true
Expand All @@ -23,6 +24,11 @@ export const mutations: MutationTree<LocalesStateInterface> = {
}
}

/**
* @name actions
* @description
* Vuex Locales actions
*/
export const actions: ActionTree<LocalesStateInterface, RootStateInterface> = {
selectLocale({ commit }, localeId: string) {
commit(MutationType.locales.selectLocale, localeId)
Expand Down
2 changes: 0 additions & 2 deletions composition-api/src/store/locales/initialState.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { LocalesStateInterface } from '@/models/store'
import { LocaleInfoInterface } from '@/models/localization/LocaleInfo.interface'
import { LocalStorageKeys } from '@/models/local-storage/LocalStorageKeys'
import { i18n } from '@/plugins/vue-i18n-next-plugin'
import { config } from '@/config'

// drive locales with config
Expand Down
72 changes: 67 additions & 5 deletions composition-api/src/store/root/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,27 @@ import {
RootStoreInterface,
RootStoreModel,
StoreModuleNames,
MutationType
MutationType,
// GEN-IMPORTS-STATE-INTERFACE
ItemsStateInterface,
LocalesStateInterface
} from '@/models/store'


import { initialRootState } from './initialState'

import { LocalStorageKeys } from '@/models/local-storage/LocalStorageKeys'

// GEN-IMPORTS
// import each Vuex module
// GEN-IMPORTS-STATE
import { itemsState } from '@/store/items'
import { localesState } from '@/store/locales'

// Vuex store options to build our modularized namespaced store
/**
* @name storeOptions
* @description
* Vuex store options to build our modularized namespaced store
*/
const storeOptions: StoreOptions<RootStateInterface> = {
state: initialRootState,

Expand All @@ -32,14 +40,68 @@ const storeOptions: StoreOptions<RootStateInterface> = {
// Vuex Root store instance
export const store: RootStoreModel<RootStateInterface> = <any>createStore(storeOptions)

/**
* @name dispatchModuleAction
* @description
* Private helper to dispatch an action to a Vuex module from one place
* So we keep the string interpolation for `${moduleName}/${actionName}` in one place only
* and be able to dispatch action with less code in a strongly-type way
* @param moduleName
* @param actionName
* @param params
*/
function dispatchModuleAction<T>(moduleName: string, actionName: string, params?: T): void {
store.dispatch(`${moduleName}/${actionName}`, params)
}

// create individual module-store wrappers by app domain
// where each wrapper returns the corresponding module state from the Vuex Store
// and has an action<T> that allows us to dispatch the action with less
// code and in astrongly-typed way
/**
* @name itemsStore
* @description
* The items store wrapper that returns the itemsState and exposes a generic action<T> method
*/
const itemsStore = {
get state(): ItemsStateInterface {
return store.state.itemsState
},
action<T>(actionName: string, params?: T): void {
dispatchModuleAction(StoreModuleNames.itemsState, actionName, params)
}
}

/**
* @name localesStore
* @description
* The locales store wrapper that retuns the localesState and exposes a generic action<T> method
*/
const localesStore = {
get state(): LocalesStateInterface {
return store.state.localesState
},
action<T>(actionName: string, params?: T): void {
dispatchModuleAction(StoreModuleNames.localesState, actionName, params)
}
}

// export our wrappers using the composition API convention (i.e. useXYZ)
export const useItemsStore = () => {
return itemsStore
}
export const useLocalesStore = () => {
return localesStore
}

// for the current locale,
// try using the last user's preferred locale
// if available from localStorage
const userPreferredLocaleId: string = localStorage.getItem(LocalStorageKeys.locale) || ''
if (userPreferredLocaleId.length > 0) {
// change locale selected
store.dispatch(
`${StoreModuleNames.localesState}/${MutationType.locales.selectLocale}`,
localesStore.action(
MutationType.locales.selectLocale,
userPreferredLocaleId
)
}
11 changes: 6 additions & 5 deletions composition-api/src/views/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<script lang="ts">
import { defineComponent, computed, onMounted } from 'vue'
import { store } from '@/store'
import { useItemsStore } from '@/store'
import { MutationType, StoreModuleNames } from '@/models/store'
import ItemsListComponent from '@/components/items/ItemsList.component.vue'
import { ItemInterface } from '@/models/items/Item.interface'
Expand All @@ -17,19 +17,20 @@
ItemsListComponent
},
setup() {
const itemsStore = useItemsStore()
const items = computed(() => {
return store.state.itemsState.items
return itemsStore.state.items
})
const loading = computed(() => {
return store.state.itemsState.loading
return itemsStore.state.loading
})
onMounted(() => {
store.dispatch(`${StoreModuleNames.itemsState}/${MutationType.items.loadItems}`)
itemsStore.action(MutationType.items.loadItems)
})
const onSelectItem = (item: ItemInterface) => {
store.dispatch(`${StoreModuleNames.itemsState}/${MutationType.items.selectItem}`, {
itemsStore.action(MutationType.items.selectItem, {
id: item.id,
selected: !item.selected
})
Expand Down

0 comments on commit f30bad3

Please sign in to comment.