-
-
Save mrcrowl/d7fd8d0369759a9fe315dbf27dc1bced to your computer and use it in GitHub Desktop.
| // path: store/basket/basket.ts (module) | |
| import { RootState } from "../../store" | |
| import inventory, { Product } from "../inventory/inventory" | |
| export interface Item { productId: string, quantity: number } | |
| export interface DisplayItem { product: Product, quantity: number } | |
| export interface BasketState { items: Item[], isLoading: boolean } | |
| const initialBasketState: BasketState = { items: [], isLoading: false } | |
| const b = getStoreBuilder<RootState>().module("basket", initialBasketState) | |
| // getters | |
| const numberOfItemsGetter = b.read(state => state.items.length, "numberOfItems") | |
| const itemsGetter = b.read(state => | |
| { | |
| const displayItems: DisplayItem[] = state.items.map(item => | |
| { | |
| return { | |
| product: inventory.getProductById(item.productId), | |
| quantity: item.quantity | |
| } | |
| }) | |
| return displayItems | |
| }) | |
| // mutations | |
| function appendItem(state: BasketState, payload: { productId: string, quantity: number }) | |
| { | |
| state.items.push({ | |
| productId: payload.productId, | |
| quantity: payload.quantity | |
| }) | |
| } | |
| function setIsLoading(state: BasketState, payload: { isLoading: boolean }) | |
| { | |
| state.isLoading = payload.isLoading | |
| } | |
| // action | |
| async function restoreSavedBasket(context: BareActionContext<BasketState, RootState>) | |
| { | |
| const savedBasketId = localStorage["basketId"] | |
| try | |
| { | |
| basket.commitSetIsLoading({ isLoading: true }) | |
| const { data: savedBasket } = await axios.get(`//chips-store.com/get-saved-basket/${savedBasketId}`, { responseType: "json" }) | |
| const items: Item[] = savedBasket.items | |
| items.forEach(item => basket.commitAppendItem(item)) | |
| } | |
| finally | |
| { | |
| basket.commitSetIsLoading({ isLoading: false }) | |
| } | |
| } | |
| // state | |
| const stateGetter = b.state() | |
| // exported "basket" module interface | |
| const basket = { | |
| // state | |
| get state() { return stateGetter() }, | |
| // getters (wrapped as real getters) | |
| get items() { return itemsGetter() }, | |
| get numberOfItems() { return numberOfItemsGetter() }, | |
| // mutations | |
| commitAppendItem: b.commit(appendItem), | |
| commitSetIsLoading: b.commit(setIsLoading), | |
| // actions | |
| dispatchRestoreSavedBasket: b.dispatch(restoreSavedBasket) | |
| } | |
| export default basket |
| // path: store/inventory/inventory.ts (module) | |
| import { getStoreBuilder, BaseActionContext } from "vuex-typex" | |
| import { Store } from "vuex" | |
| import { RootState } from "../../store" | |
| import axios from "axios" | |
| export interface InventoryState { productsById: { [productId: string]: Product } } | |
| export interface Product { id: string, name: string } | |
| const initialInventoryState: InventoryState = { | |
| productsById: { | |
| "fritos": { id: "fritos", name: "Fritos Corn Chips, Chili Cheese" }, | |
| "doritos": { id: "doritos", name: "Doritos Nacho Cheese Flavored Tortilla Chips" }, | |
| "cheetos": { id: "cheetos", name: "Cheetos Crunchy Cheese Flavored Snacks" }, | |
| "tostitos": { id: "tostitos", name: "Tostitos Original Restaurant Style Tortilla Chips" } | |
| } | |
| } | |
| const p = getStoreBuilder<RootState>().module("product", initialInventoryState) | |
| const getProductByIdGetter = p.read(state => (id: string) => state.productsById[id], "getProductById") | |
| // state | |
| const stateGetter = p.state() | |
| // exported "inventory" module interface | |
| const inventory = { | |
| // state | |
| get state() { return stateGetter() }, | |
| // getter as method | |
| getProductById(id: string) | |
| { | |
| return getProductByIdGetter()(id) | |
| } | |
| } | |
| export default inventory |
| // path: store/store.ts (root store definition) | |
| import Vue from 'vue' | |
| import Vuex, { Store } from 'vuex' | |
| import inventory from "./inventory/inventory" | |
| import basket from "./basket/basket" | |
| export interface RootState | |
| { | |
| basket: BasketState | |
| inventory: InventoryState | |
| } | |
| Vue.use(Vuex) | |
| const store: Store<RootState> = getStoreBuilder<RootState>().vuexStore() | |
| export default store // <-- "store" to provide to root Vue |
| // path: app.ts (root Vue) | |
| import Vue from 'vue' | |
| import Vuex from 'vuex' | |
| import store from './store/store' | |
| import app_html from './app.html' | |
| const app = new Vue({ | |
| el: '#app', | |
| template: app_html, | |
| store | |
| }) | |
| export default app |
| // path: components/basket/basketDisplay.ts (component) | |
| import basket from "../../store/basket/basket" | |
| @Component({ template: basket_display_html }) | |
| export class BasketDisplay extends Vue | |
| { | |
| get isLoading() { return basket.state.isLoading } | |
| get items() { return basket.items } | |
| get numberOfItems() { return basket.numberOfItems } | |
| restoreSavedBasket() | |
| { | |
| basket.dispatchRestoreSavedBasket() | |
| } | |
| addToBasket(productId: string, quantity: number = 1) | |
| { | |
| basket.commitAppendItem({ productId, quantity }) | |
| } | |
| } |
I'm getting the following error:
"Can't add module after vuexStore() has been called"
Anybody else getting that? Any suggestions on how to get passed that?
Wow! This works like magic to me. Good work!
@dmarg, did you ever solve this?
Trying to migrate to VueCLI 3, and I am seeing the same error on the same codebase which makes me think the issue is with the CLI -- or more the upgrade to Typescript 3.x.
@SvenPam also having this issue.. any ideas?
Edit
I fixed this by changing my store.ts file from:
import { AuthState } from './modules/auth'to
import { AuthState } from './modules/auth'
import './modules/auth'It don't work with SSR!
All sessions gets same store!
In this example I get the error:
70:44 Argument of type '(context: ActionContext<BasketState, RootState>) => Promise<void>' is not assignable to parameter of type 'ActionHandler<BasketState, RootState, {}, void>'.when I try to use your basket state as you have it setup. Any thoughts?
@Gregoyle I ran into the same error, did you ever find out why you got this error?
Figured it out context: ActionContext<BasketState, RootState> needs changing to context: BareActionContext<BasketState, RootState>
It don't work with SSR!
All sessions gets same store!
Yes, sorry. Never tried this with SSR tbh.
@Schteele.. thanks. I've updated it to use BareActionContext now.
@b12f, I ran into the same issue with the modules being shaken out by typescript. Trying to use the module in the router, caused issues since it wasn't defined.
One solution I came up with, since I'm not using the module in the store.ts, was to import the module for side-effects. This stops typescript from shaking them out.
This means changing
to
If you check here: https://www.typescriptlang.org/docs/handbook/modules.html and look for "Import a module for side-effects only"
Hope this helps others.