How to do final state injection with Vue SRR with V8Js - vue.js

The Vue SSR guide is mainly written for running a nodejs server and just touches on using V8Js in the final chapter.
It does have a section on final state injection but this doesn't work in the context of V8Js.
How can we pass the Vuex state from server to client side when using V8Js?

First in entry-server.js we need to print not just the app, but also the Vuex state.
import { createApp } from './app'
new Promise((resolve, reject) => {
const { app, router, store } = createApp()
router.push(url)
// Wait until router has resolved possible async components and hooks.
router.onReady(() => {
const matchedComponents = router.getMatchedComponents()
// No matched routes, reject with 404.
if (matchedComponents.length === 0) {
return reject({ code: 404 })
}
resolve(app)
}, reject)
})
.then(app => {
renderVueComponentToString(app, (err, res) => {
// Only now the app has rendered and all components have had a chance to
// populate the Vuex store can we set the initial state.
const initialState = JSON.stringify(app.$store.state)
print(`<script>window.__INITIAL_STATE__ = ${initialState}</script>\n\r`)
// Print the markup for the app.
print(res)
})
})
.catch((err) => {
print(err)
})
And then in entry-client.js we need to populate the Vuex store with that data:
import { createApp } from './app'
const { app, router, store } = createApp()
if (window.__INITIAL_STATE__) {
// Initialize the store state with the data injected from the server.
store.replaceState(window.__INITIAL_STATE__)
}
router.onReady(() => {
app.$mount('#app')
})

Related

vue3 creating global variables returns undefined

I am currently working on upgrading a project to Vue3. Inside the project we have several files that create global variables inside a boot directory:
src/boot/auth.js
src/boot/axios.js
src/boot/cranky.js
....
Each of these files creates global variables that I am using throughout the project. For example my auth.js file:
import auth from '../app/auth';
import { createApp } from 'vue';
export default async ({
router,
Vue
}) => {
const app = createApp({});
app.config.globalProperties.$auth = auth;
// Vue.prototype.$auth = auth;
//This works with logic in MainLayout to permit users to see particular screens
//after they are logged in.
router.beforeResolve((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// eslint-disable-next-line no-unused-vars
let user;
if (app.config.globalProperties.$auth.isAuthenticated()) {
// TODO: record screens viewed by router url
next();
} else {
next({
path: '/home'
});
}
}
next();
});
};
After research and reading I understand that global variables are created like this:
app.config.globalProperties.$auth = auth;, Which then should be called from other component files using this.$auth. In my case, however, this returns as undefined.
My theory is that there is an issue with my createapp. Am I calling that correctly, or am I missing something else?
Thanks.
EDIT
added code requested by #tony19
The following the full script tag. My understanding and I probably am wrong, is that the this works as a global in vue3.
<script>
import HeaderScoreIndicator from '../components/HeaderScoreIndicator.vue';
import ExitResearchControl from '../components/ExitResearchControl.vue';
import {mq} from 'src/boot/mq'
export default {
components: {
HeaderScoreIndicator,
ExitResearchControl
},
data: function () {
return {
tab: 'denial',
signedIn: false,
username: null,
user: null
};
},
computed: {
/*
These 4 functions bind the colors to the background elements
*/
title: function () {
return this.$store.state.globals.title;
},
toolbarStyle: function () {
return 'padding-top: 10px; background-color: ' + this.$store.state.globals.toolbarColor;
},
footerStyle: function () {
return `background-color: ${this.$store.state.globals.toolbarColor};`;
},
backgroundStyle: function () {
if(!this.$mq.phone){
return `background: linear-gradient(180deg, ${this.$store.state.globals.backgroundGradientTop} 0%, ${this.$store.state.globals.backgroundGradientBottom} 100%);
display: flex;
justify-content: center;`;
} else{
return `background: linear-gradient(180deg, ${this.$store.state.globals.backgroundGradientTop} 0%, ${this.$store.state.globals.backgroundGradientBottom} 100%);`;
};
},
limitSize: function(){
if(!this.$mq.phone){
return 'max-width: 1023px; width: 100%;'
} else{
return
};
}
},
/*
In the beforeCreate function, we're setting up an event listener to detect
when we've logged in. At the successful login we can push the user to
the correct screen.
*/
beforeCreate: function () {
console.log(this.$auth);
return this.$auth.getState().then(data => {
if (this.$auth.isAuthenticated()) {
this.username = this.$auth.getEmail();
return this.initialize(data).then(() => this.signedIn = true);
} else {
this.signedIn = false;
return newUserLanguageSelection()
}
}).catch(e => {
this.signedIn = false;
console.error(e);
});
},
methods: {
newUserLanguageSelection: function(){
if(localStorage.getItem('languageSet') != 'true'){
return this.$router.push('/language');
}
},
initialize: function (data) {
this.$store.commit('globals/dataLoaded');
this.$store.commit('scoring/setUsername', this.username);
return this.$store.dispatch('scoring/initializeScoring', { points: data.score | 0 })
.then(() => {
if(this.$store.state.globals.testQuiz){
return;
} else if(localStorage.getItem('languageSet') != 'true'){
return this.$router.push('/language');
}else if (data.seenAnalyticsDialog == true) {
return this.$router.push('/home');
} else {
return this.$router.push('/consent');
}
})
.catch(e => {
//silence NavigationDuplicated errors
if (e.name != "NavigationDuplicated")
throw e;
});
}
}
};
</script>
In the previous Vue 2 project, it looks like Vue was passed to the auth.js module so that it could attach $auth to the Vue prototype.
However, in an attempt to upgrade that particular code in the auth.js module to Vue 3, you ignore the Vue argument, and create a throwaway application instance to create the $auth global property:
// src/boot/auth.js
export default async ({
router,
Vue,
}) => {
const app = createApp({}); ❌ local app instance
app.config.globalProperties.$auth = auth;
⋮
}
But that won't work because the app instance is not somehow hooked into the application instance you'll eventually mount in main.js.
Fixing the upgrade
Your main.js probably looks similar to this:
// src/main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import auth from './boot/auth';
auth({ router, Vue });
⋮
Vue.createApp(App).mount('#app');
To correctly upgrade the original code, you should pass the app instance from main.js to auth.js:
// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import auth from './boot/auth';
const app = createApp(App);
auth({ router, app });
⋮
app.mount('#app');
And use that app in the auth.js module:
// src/boot/auth.js
export default async ({
router,
app, 👈
}) => {
👇
app.config.globalProperties.$auth = auth;
⋮
}
Convert to Vue plugin
Assuming you don't need to await the return of the auth.js module, you could make that module a Vue plugin instead, which automatically receives the app instance as the first argument. The second argument receives the options passed in from app.use():
// src/boot/auth.js
export default {
install(app, { router }) {
app.config.globalProperties.$auth = auth;
router.beforeResolve(⋯);
}
}
// src/main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import auth from './boot/auth';
const app = createApp(App);
app.use(auth, { router });
⋮
app.mount('#app');

vue 3 Server Side Rendering with Vuex and Router

I have created a Vue3 application using the Vue CLI to create my application with Vuex and Router. The application runs well.
Note: I followed this useful doc for the Vuex with Vue3 https://blog.logrocket.com/using-vuex-4-with-vue-3/
Requirement Now I would like to change my Vue3 application to have Server Side Rendering support(i.e. SSR).
I watched this awesome video on creating an SSR application using Vue3 : https://www.youtube.com/watch?v=XJfaAkvLXyU and I can create and run a simple application like in the video. However I am stuck when trying to apply it to my main Vue3 app.
My current sticking point is how to specify the router and vuex on the server code.
My Code
The client entry file (src/main.js) has the following
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
createApp(App).use(store).use(router).mount('#app');
The server entry file (src/main.server.js) currently has the following
import App from './App.vue';
export default App;
And in the express server file (src/server.js) it currently has
const path = require('path');
const express = require('express');
const { createSSRApp } = require('vue');
const { renderToString } = require('#vue/server-renderer');
...
...
server.get('*', async (req, res) => {
const app = createSSRApp(App);
const appContent = await renderToString(app);
I need to change this code so that the app on the server side is using the router and vuex like it is on the client.
Issues
In the express server file i can not import the router and vuex like in the client entry file as it fails due to importing outside a module, therefore in the express server I can not do the following
const app = createSSRApp(App).use(store).use(router);
I have tried changing the server entry file (src/main.server.js) to the following, but this does not work either.
import App from './App.vue';
import router from './router';
import store from './store';
const { createSSRApp } = require('vue');
export default createSSRApp(App).use(store).use(router);
Does anyone know how to do SSR in Vue 3 when your app is using Vuex and Router.
How i did this in Vue 2 is below and what i am trying to change over to Vue 3
My Vue2 version of this application had the following code
src/app.js creates the Vue component with the router and store specified
Client entry file (src/client/main.js) gets the app from app.js, prepopulates the Vuex store with the data serialized out in the html, mounts the app when the router is ready
import Vue from 'vue';
import { sync } from 'vuex-router-sync';
import App from './pages/App.vue';
import createStore from './vuex/store';
import createRouter from './pages/router';
export default function createApp() {
const store = createStore();
const router = createRouter();
sync(store, router);
const app = new Vue({
router,
store,
render: (h) => h(App),
});
return { app, router, store };
}
Server Entry file (src/server/main.js), gets the app from app.js, get the matched routes which will call the "serverPrefetch" on each component to get its data populated in the Vuex store, then returns the resolve promise
import createApp from '../app';
export default (context) => new Promise((resolve, reject) => {
const { app, router, store } = createApp();
router.push(context.url);
router.onReady(() => {
const matchedComponents = router.getMatchedComponents();
if (!matchedComponents.length) {
return reject(new Error('404'));
}
context.rendered = () => {
context.state = store.state;
};
return resolve(app);
}, reject);
});
Express server (/server.js) uses the bundle renderer to render the app to a string to put in the html
const fs = require('fs');
const express = require('express');
const { createBundleRenderer } = require('vue-server-renderer');
const dotenv = require('dotenv');
dotenv.config();
const bundleRenderer = createBundleRenderer(
require('./dist/vue-ssr-server-bundle.json'),
{
template: fs.readFileSync('./index.html', 'utf-8'),
},
);
const server = express();
server.use(express.static('public'));
server.get('*', (req, res) => {
const context = {
url: req.url,
clientBundle: `client-bundle.js`,
};
bundleRenderer.renderToString(context, (err, html) => {
if (err) {
if (err.code === 404) {
res.status(404).end('Page not found');
} else {
res.status(500).end('Internal Server Error');
}
} else {
res.end(html);
}
});
});
const port = process.env.PORT || 3000
server.listen(port, () => {
console.log(`Listening on port ${port}`);
});
I have managed to find the solution to this thanks to the following resources:
Server Side Rendering with Vue.js 3 video: https://www.youtube.com/watch?v=XJfaAkvLXyU&feature=youtu.be and git repos: https://github.com/moduslabs/vue3-example-ssr
SSR + Vuex + Router app : https://github.com/shenron/vue3-example-ssr
migrating from Vue 2 to Vue 3
https://v3-migration.vuejs.org/breaking-changes/introduction.html
migrating from VueRouter 3 to VueRouter 4
https://next.router.vuejs.org/guide/migration/
migrating from Vuex 3 to Vuex 4
https://next.vuex.vuejs.org/guide/migrating-to-4-0-from-3-x.html
client entry file (src/main.js)
import buildApp from './app';
const { app, router, store } = buildApp();
const storeInitialState = window.INITIAL_DATA;
if (storeInitialState) {
store.replaceState(storeInitialState);
}
router.isReady()
.then(() => {
app.mount('#app', true);
});
server entry file (src/main-server.js)
import buildApp from './app';
export default (url) => new Promise((resolve, reject) => {
const { router, app, store } = buildApp();
// set server-side router's location
router.push(url);
router.isReady()
.then(() => {
const matchedComponents = router.currentRoute.value.matched;
// no matched routes, reject with 404
if (!matchedComponents.length) {
return reject(new Error('404'));
}
// the Promise should resolve to the app instance so it can be rendered
return resolve({ app, router, store });
}).catch(() => reject);
});
src/app.js
import { createSSRApp, createApp } from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
const isSSR = typeof window === 'undefined';
export default function buildApp() {
const app = (isSSR ? createSSRApp(App) : createApp(App));
app.use(router);
app.use(store);
return { app, router, store };
}
server.js
const serialize = require('serialize-javascript');
const path = require('path');
const express = require('express');
const fs = require('fs');
const { renderToString } = require('#vue/server-renderer');
const manifest = require('./dist/server/ssr-manifest.json');
// Create the express app.
const server = express();
// we do not know the name of app.js as when its built it has a hash name
// the manifest file contains the mapping of "app.js" to the hash file which was created
// therefore get the value from the manifest file thats located in the "dist" directory
// and use it to get the Vue App
const appPath = path.join(__dirname, './dist', 'server', manifest['app.js']);
const createApp = require(appPath).default;
const clientDistPath = './dist/client';
server.use('/img', express.static(path.join(__dirname, clientDistPath, 'img')));
server.use('/js', express.static(path.join(__dirname, clientDistPath, 'js')));
server.use('/css', express.static(path.join(__dirname, clientDistPath, 'css')));
server.use('/favicon.ico', express.static(path.join(__dirname, clientDistPath, 'favicon.ico')));
// handle all routes in our application
server.get('*', async (req, res) => {
const { app, store } = await createApp(req);
let appContent = await renderToString(app);
const renderState = `
<script>
window.INITIAL_DATA = ${serialize(store.state)}
</script>`;
fs.readFile(path.join(__dirname, clientDistPath, 'index.html'), (err, html) => {
if (err) {
throw err;
}
appContent = `<div id="app">${appContent}</div>`;
html = html.toString().replace('<div id="app"></div>', `${renderState}${appContent}`);
res.setHeader('Content-Type', 'text/html');
res.send(html);
});
});
const port = process.env.PORT || 8080;
server.listen(port, () => {
console.log(`You can navigate to http://localhost:${port}`);
});
vue.config.js
used to specify the webpack build things
const ManifestPlugin = require('webpack-manifest-plugin');
const nodeExternals = require('webpack-node-externals');
module.exports = {
devServer: {
overlay: {
warnings: false,
errors: false,
},
},
chainWebpack: (webpackConfig) => {
webpackConfig.module.rule('vue').uses.delete('cache-loader');
webpackConfig.module.rule('js').uses.delete('cache-loader');
webpackConfig.module.rule('ts').uses.delete('cache-loader');
webpackConfig.module.rule('tsx').uses.delete('cache-loader');
if (!process.env.SSR) {
// This is required for repl.it to play nicely with the Dev Server
webpackConfig.devServer.disableHostCheck(true);
webpackConfig.entry('app').clear().add('./src/main.js');
return;
}
webpackConfig.entry('app').clear().add('./src/main-server.js');
webpackConfig.target('node');
webpackConfig.output.libraryTarget('commonjs2');
webpackConfig.plugin('manifest').use(new ManifestPlugin({ fileName: 'ssr-manifest.json' }));
webpackConfig.externals(nodeExternals({ allowlist: /\.(css|vue)$/ }));
webpackConfig.optimization.splitChunks(false).minimize(false);
webpackConfig.plugins.delete('hmr');
webpackConfig.plugins.delete('preload');
webpackConfig.plugins.delete('prefetch');
webpackConfig.plugins.delete('progress');
webpackConfig.plugins.delete('friendly-errors');
// console.log(webpackConfig.toConfig())
},
};
src/router/index.js
import { createRouter, createMemoryHistory, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
import About from '../views/About.vue';
const isServer = typeof window === 'undefined';
const history = isServer ? createMemoryHistory() : createWebHistory();
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
component: About,
},
];
const router = createRouter({
history,
routes,
});
export default router;
src/store/index.js
import Vuex from 'vuex';
import fetchAllBeers from '../data/data';
export default Vuex.createStore({
state() {
return {
homePageData: [],
};
},
actions: {
fetchHomePageData({ commit }) {
return fetchAllBeers()
.then((data) => {
commit('setHomePageData', data.beers);
});
},
},
mutations: {
setHomePageData(state, data) {
state.homePageData = data;
},
},
});
Github sample code
I found I needed to go through the building the code step by step doing just SSR, just Router, just Vuex and then put it all together.
My test apps are in github
https://github.com/se22as/vue-3-with-router-basic-sample
"master" branch : just a vue 3 app with a router
"added-ssr" branch : took the "master" branch and added ssr code
"add-just-vuex" branch : took the "master" branch and added vuex code
"added-vuex-to-ssr" branch : app with router, vuex and ssr.
You can also use Vite which has native SSR support and, unlike Webpack, works out-of-the-box without configuration.
And if you use vite-plugin-ssr then it's even easier.
The following highlights the main parts of vite-plugin-ssr's Vuex example
<template>
<h1>To-do List</h1>
<ul>
<li v-for="item in todoList" :key="item.id">{{item.text}}</li>
</ul>
</template>
<script>
export default {
serverPrefetch() {
return this.$store.dispatch('fetchTodoList');
},
computed: {
todoList () {
return this.$store.state.todoList
}
},
}
</script>
import Vuex from 'vuex'
export { createStore }
function createStore() {
const store = Vuex.createStore({
state() {
return {
todoList: []
}
},
actions: {
fetchTodoList({ commit }) {
const todoList = [
{
id: 0,
text: 'Buy milk'
},
{
id: 1,
text: 'Buy chocolate'
}
]
return commit('setTodoList', todoList)
}
},
mutations: {
setTodoList(state, todoList) {
state.todoList = todoList
}
}
})
return store
}
import { createSSRApp, h } from 'vue'
import { createStore } from './store'
export { createApp }
function createApp({ Page }) {
const app = createSSRApp({
render: () => h(Page)
})
const store = createStore()
app.use(store)
return { app, store }
}
import { renderToString } from '#vue/server-renderer'
import { html } from 'vite-plugin-ssr'
import { createApp } from './app'
export { render }
export { addContextProps }
export { setPageProps }
async function render({ contextProps }) {
const { appHtml } = contextProps
return html`<!DOCTYPE html>
<html>
<body>
<div id="app">${html.dangerouslySetHtml(appHtml)}</div>
</body>
</html>`
}
async function addContextProps({ Page }) {
const { app, store } = createApp({ Page })
const appHtml = await renderToString(app)
const INITIAL_STATE = store.state
return {
INITIAL_STATE,
appHtml
}
}
function setPageProps({ contextProps }) {
const { INITIAL_STATE } = contextProps
return { INITIAL_STATE }
}
import { getPage } from 'vite-plugin-ssr/client'
import { createApp } from './app'
hydrate()
async function hydrate() {
const { Page, pageProps } = await getPage()
const { app, store } = createApp({ Page })
store.replaceState(pageProps.INITIAL_STATE)
app.mount('#app')
}
Simplest example: Updated with the latest document on the Vue website.
https://github.com/ThinhVu/vue--just-ssr
You can find more examples in this repository which included more about impl SSR in the real world.
The repository not only includes naive implement in VueJs but also contains an example using Vite only, Vite + Vite-SSR-plugin, Nuxt, QuasarJS
(I'm working on it, more examples will be added later).
https://github.com/ThinhVu/vue-ssr-labs

Mocking axios in Jest returns axios is not defined

I have seen similar questions but they dont actually address what am looking for.
so am using using axios globally in app.js for my vue app like window.axios=require('axios')
then in auth.js i have this
export function login(credentials){
return new Promise((res,rej) => {
axios.post('/api/auth/login', credentials)
.then((response) => {
res(response.data);
})
.catch((err) => {
rej("Wrong email or password");
})
});
}
which works fine on the login page
however in my test script
jest.mock("axios", () => ({
post: jest.fn(() => Promise.resolve({data:{first_name:'James','last_name':'Nwachukwu','token':'abc123'}}))
}));
import axios from 'axios'
import {login} from '../helpers/auth'
it("it logs in when data is passed", async () => {
const email='super#gmail.com'
const password='secret';
const result=await login({email,password});
expect(axios.post).toBeCalledWith('/api/auth/login',{"email": "super#gmail.com", "password": "secret"})
expect(result).toEqual({first_name:'James','last_name':'Nwachukwu','token':'abc123'})
})
shows axios is not defined
but if i change auth.js to
import axios from 'axios'
export function login(credentials){
return new Promise((res,rej) => {
axios.post('/api/auth/login', credentials)
.then((response) => {
res(response.data);
})
.catch((err) => {
rej("Wrong email or password");
})
});
}
test passes. how do i run the test without having to import axios on each vue file
I had the same problem just now. I am also including axios via window.axios = require('axios'); in my app.js.
The solution is to set your axios mock on window.axios in your test. So instead of this (incorrect):
axios = {
post: jest.fn().mockName('axiosPost')
}
const wrapper = mount(Component, {
mocks: {
axios: axios
}
})
When your component code calls axios.whatever it is really calling window.axios.whatever (as I understand it), so you need to mirror that in your test environment:
window.axios = {
post: jest.fn().mockName('axiosPost')
}
const wrapper = mount(Component, {
mocks: {
axios: window.axios
}
})
And in your test:
expect(window.axios.post).toHaveBeenCalled()
The above method works fine until you want to chain then to it. In which case you need to set your mock up like this:
window.axios = {
get: jest.fn(() => {
return {
then: jest.fn(() => 'your faked response')
}
}),
}
You don't need to pass it into the component mock though, you can just mount (or shallowMount) the component as usual

in Vuex, How to load state and use data from the state when the application loads/renders first ?, I am using nuxt, Vue and Vuex as store

I am trying to load data from a JSON file into the VueX store, but the state does not get loaded until I try to refresh the VueX Store manually.
what I am trying to achieve is, before the app renders, the state should be loaded with the data.
Like before I access the homepage.
But I see on the Vue Devtools, that if set it to recording mode, then the app loads the data.
Below is code from store/index.js
//store/index.js
const exec = (method, { rootState, dispatch }, app) => {
const dispatches = [];
Object.keys(rootState).forEach(async (s) => {
dispatches.push(await dispatch(`${s}/${method}`, app));
});
return dispatches;
};
export const actions = {
nuxtServerInit(store, ctx) {
console.log('nuxtServerInit');
exec('init', store, ctx);
},
nuxtClientInit(store, ctx) {
console.log('nuxtClientInit');
exec('init', store, ctx);
},
init(store, ctx) {
console.log('nuxtInit');
exec('init', store, ctx);
},
};
store/app.js
//store/app.js
export const state = () => ({
config: {},
});
export const mutations = {
SET_CONFIG(state, config) {
state.config = config;
}
}
};
export const getters = {
config: (state) => state.config,
};
const loadConfig = ({ commit }) => {
const siteConfig = require('../config/data.json');
const appConfig = JSON.parse(JSON.stringify(siteConfig.properties));
commit('SET_CONFIG', appConfig);
};
export const actions = {
init(store, ctx) {
loadConfig(store);
},
};
Here the state is empty when the app loads. How can I access that when the app loads?
I normally call the init action of my store in the layout.
When this is too late you could also do it in a plugin, I guess.
You can use the context.store in the plugin.
// plugins/init.js
export default ({ store }) => {
store.dispatch("init")
}
// store/index.js
export actions = {
init(context) {
// ...
}
}

from same axios to different components, VueJS

Okay, I have two different components and each of those get Axios response. But I don't want to fetch data in each component separate. That's is not right, and it cause components run separate...
Updated 3
I did some changes on the code, but still having some problems. I am doing axios call with Vuex in Store.js and import it into my component. it's like below.
This is my store.js component;
import Vue from "vue";
import Vuex from "vuex";
var actions = _buildActions();
var modules = {};
var mutations = _buildMutations();
const state = {
storedData: []
};
Vue.use(Vuex);
const getters = {
storedData: function(state) {
return state.storedData;
}
};
function _buildActions() {
return {
fetchData({ commit }) {
axios
.get("/ajax")
.then(response => {
commit("SET_DATA", response.data);
})
.catch(error => {
commit("SET_ERROR", error);
});
}
};
}
function _buildMutations() {
return {
SET_DATA(state, payload) {
console.log("payload", payload);
const postData = payload.filter(post => post.userId == 1);
state.storedData = postData;
}
};
}
export default new Vuex.Store({
actions: actions,
modules: modules,
mutations: mutations,
state: state,
getters
});
Now importing it into Average component.
import store from './Store.js';
export default {
name:'average',
data(){
return{
avg:"",
storedData: [],
}
},
mounted () {
console.log(this.$store)
this.$store.dispatch('fetchDatas')
this.storedData = this.$store.dispatch('fetchData')
},
methods: {
avgArray: function (region) {
const sum = arr => arr.reduce((a,c) => (a += c),0);
const avg = arr => sum(arr) / arr.length;
return avg(region);
},
},
computed: {
mapGetters(["storedData"])
groupedPricesByRegion () {
return this.storedData.reduce((acc, obj) => {
var key = obj.region;
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(obj.m2_price);
return acc;
}, {});
},
averagesByRegion () {
let arr = [];
Object.entries(this.groupedPricesByRegion)
.forEach(([key, value]) => {
arr.push({ [key]: Math.round(this.avgArray(value)) });
});
return arr;
},
}
}
I can see the data stored in the console. But there are errors too. I can't properly pass the data in myComponent
https://i.stack.imgur.com/J6mlV.png
if you don't want use vuex to distrib data maybe you can try eventBus, when you get data form the axios respose #emit the event and in another component #on this event
The issue is that
To resolve the error you're getting, Below are the steps.
Import your store file inside the file where your Vue Instance is initialized.
// Assuming your store file is at the same level
import store from './store';
Inside your, Add store object inside your Vue Instance
function initApp(appNode) {
new Vue({
el: appNode,
router: Router,
store // ES6 sytax
});
}
There you go, you can now access your store from any component.
UPDATE: For Second Error
Instead of changing data inside your component, change it inside mutation in the store because you do not want to write the same login in other components where the same method is used.
Hence,
computed: {
...mapGetters(["storedData"]),
anotherFunction() {
// Function implementation.
}
}
Inside your mutation set the data.
SET_DATA(state, payload) {
console.log("payload", payload);
state.storedData = payload;
}
Inside getters, you can perform what you were performing inside your computed properties.
storedData: function(state) {
const postData = state.storedData.filter(post => post.userId == 1);
return postData;
}
Vuex Official docs
Here is the working codesandbox
Hope this helps!