Pre-fetch data using vuex and vue-resource - vue.js

I'm building an app following this structure: http://vuex.vuejs.org/en/structure.html
My components/App.vue like this:
<template>
<div id="app">
<course :courses="courses"></course>
</div>
</template>
<script>
import Course from './course.vue'
import { addCourses } from '../vuex/actions'
export default {
vuex: {
getters: {
courses: state => state.courses,
},
actions: {
addCourses,
}
},
ready() {
this.addCourses(this.fetchCourses())
},
components: { Course },
methods: {
fetchCourses() {
// what do I have to do here
}
}
}
</script>
How can I fetch the data and set it to the state.courses ?
Thanks

I've just figured it out:
in /components/App.vue ready function, I just call:
ready() {
this.addCourses()
},
in vuex/actions.js:
import Vue from 'vue'
export const addCourses = ({ dispatch }) => {
Vue.http.get('/api/v1/courses')
.then(response => {
let courses = response.json()
courses.map(course => {
course.checked = false
return course
})
dispatch('ADD_COURSES', courses)
})
}
and in vuex/store.js:
const mutations = {
ADD_COURSES (state, courses) {
state.courses = courses
}
}

Related

Problems with chart.js redrawing or not redrawing graphs

I am Japanese. Therefore, my sentences may be strange. Please keep that in mind.
I am writing code using vue.js, vuex, vue-chart.js and vue-chart.js to display the population of each prefecture of Japan when checked.I’m code is written to redraw the graph when the input element for each prefecture is checked.However, it does not redraw when checked.Also, it may redraw after half of the check.I believe this phenomenon can be confirmed from the following URL.
https://yumemi-coding.web.app/
※There are no errors.
Here's a question: what causes the graphs to redraw or not? Also, how can I code to remedy this?
What I have done to counteract the cause is as follows
I went to the official website and used the rendering process as a reference.
 URL:https://vue-chartjs.org/migration-guides/#new-reactivity-system
 => The way we did it was right.
We thought there was a problem with VueX and coded in a way that did not use it. => There was nothing wrong with vuex.
TopFroont.vue
<template>
<div class="Bar_area">
<Bar :options="chartOptions" :data="chartData" class="Bar_item" />
</div>
</template>
<script>
import { Bar } from "vue-chartjs"
import { Chart as ChartJS, registerables } from "chart.js"
ChartJS.register(...registerables)
export default {
name: "BarChart",
components: { Bar },
data() {
return {
chartOptions: {
responsive: true,
},
}
},
computed: {
chartData() {
return {
labels: this.$store.state.years,
datasets: this.$store.state.prefectures,
}
},
},
}
</script>
NaviBar.vue
<template>
<div class="navApp">
<ul>
<li v-for="(pref, index) in prefData" :key="index" class="pref_itemBox">
<label>
<input type="checkbox" #change="checkItem(pref)" />
<span class="pref_text">{{ pref.prefName }}</span>
</label>
</li>
</ul>
</div>
</template>
<script>
import resasInfo from "#/library/resas.js"
import axios from "axios"
export default {
data() {
return {
resasInfo: resasInfo,
url: resasInfo.url_prefectures,
api: resasInfo.api,
prefData: [],
prefectures: [],
}
},
async created() {
const request_Header = {
headers: { "X-API-KEY": this.api.key },
}
await axios.get(this.url, request_Header).then((res) => {
const value = res.data.result
this.prefData.push(...value)
})
},
methods: {
checkItem(pref) {
// チェックされてる都道府県のみを配列に入れる
const isExistencePref = this.prefectures.indexOf(pref)
isExistencePref === -1
? this.prefectures.push(pref)
: this.prefectures.splice(isExistencePref, 1)
this.$store.dispatch("getPrefectures", this.prefectures)
},
},
}
</script>
vuex => store/index.js
import axios from "axios"
import { createStore } from "vuex"
import createPersistedState from "vuex-persistedstate"
export default createStore({
state: {
prefectures: [],
years: [],
},
mutations: {
getPrefs(state, payload) {
state.prefectures = payload
},
getYears(state, payload) {
state.years = payload
},
},
actions: {
getPrefectures({ commit }, payload) {
// payload => 各都道府県のprefCode + prefName
const allPrefecture_Data = []
const result = payload.map(async (el) => {
const prefCode_data = el.prefCode
axios
.get(
`https://opendata.resas-portal.go.jp/api/v1/population/composition/perYear?prefCode=${prefCode_data}&cityCode=-`,
{
headers: {
"X-API-KEY": "5RDiLdZKag8c3NXpEMb1FcPQEIY3GVwgQwbLqFIx",
},
}
)
.then((res) => {
const value = res.data.result.data[0].data
const TotalPopulation_Year = []
const TotalPopulation_Data = []
// 都道府県の総人口データと年データを各配列に入れ込む
value.forEach((element) => {
TotalPopulation_Data.push(element.value)
TotalPopulation_Year.push(element.year)
})
// rgbaを自動生成する関数 => backgroundColor
const generateRGBA = () => {
const r = Math.floor(Math.random() * 256)
const g = Math.floor(Math.random() * 256)
const b = Math.floor(Math.random() * 256)
const a = 0.8
return `rgba(${r}, ${g}, ${b}, ${a})`
}
// chart.jsに入れ込むデータ
const prefData = {
label: el.prefName,
data: TotalPopulation_Data,
backgroundColor: generateRGBA(),
}
allPrefecture_Data.push(prefData)
commit("getPrefs", allPrefecture_Data)
commit("getYears", TotalPopulation_Year)
})
.catch((err) => {
console.log(err)
})
})
return result
},
},
plugins: [createPersistedState()],
getters: {},
modules: {},
})

Vue Test Utils mount in multiple tests

I am testing my Vue App using Vue Test Utils and Jest. Below is my dashboard component.
<template>
<div class="dashboard-v2">
<div class="component-container">
<component :loading="loading" :key="identifier" :is="currentTab" />
</div>
<SnackBar
v-on:snackBarHide="displaySnackBar = false"
:text="snackBarText"
:show="displaySnackBar"
:type="snackBarType"
/>
</div>
</template>
<script>
import { mapState } from "vuex";
import "#/shared/chart-kick";
import EventBus from "#/shared/event-bus";
import Tabs from "./helpers/Tabs";
import Summary from "./Summary/Index";
import { filters } from "../helpers/filters-details";
import SnackBar from "#/shared/components/SnackBar.vue";
export default {
components: {
Tabs,
Summary,
SnackBar
},
data() {
return {
identifier: +new Date(),
loading: false,
filtersLoading: false,
displaySnackBar: false,
snackBarText: "",
snackBarType: ""
};
},
mounted() {
if (!this.projects.length) this.fetchFilterData();
EventBus.$on("CLEAR_ALL", () => {
this.identifier = +new Date();
this.$store.commit(`dashboardV2/UPDATE_FILTER_STATE`, {});
});
EventBus.$on("filterChange", () => {
this.getExecData();
});
},
computed: {
...mapState("dashboardV2", [
"projects",
"currentTab",
"selectedFilters",
"timeFilter"
])
},
methods: {
fetchFilterData() {
this.filtersLoading = true;
this.$store
.dispatch("dashboardV2/GET_EXEC_FILTER_DATA")
.catch(() => {
this.displaySnackBar = true;
this.snackBarText = "There was some problem while fetching data";
this.snackBarType = "failure";
})
.finally(() => {
this.filtersLoading = false;
});
this.getExecData();
},
getExecData() {
this.loading = true;
let params = {
time_bucket: this.timeFilter,
time_zone_offset: new Date().getTimezoneOffset()
};
filters.map(e => {
params[e.query] = this.selectedFilters[e.value]
? this.selectedFilters[e.value].id
: null;
});
this.$store
.dispatch("dashboardV2/GET_EXEC_DATA", params)
.catch(() => {
this.displaySnackBar = true;
this.snackBarText = "There was some problem while fetching data";
this.snackBarType = "failure";
})
.finally(() => (this.loading = false));
}
}
};
</script>
<style lang="scss" scoped>
#import "#/styles/dashboard.scss";
</style>
Then this is my test file
import Main from "../Main.vue";
import mergeWith from "lodash.mergewith";
import { customizer, createWrapper } from "#/shared/test-helper";
import Vuex from "vuex";
import EventBus from "#/shared/event-bus";
let GET_EXEC_DATA = jest.fn(() => Promise.resolve());
let GET_EXEC_FILTER_DATA = jest.fn(() => Promise.resolve());
export const createStore = (overrides) => {
let storeOptions = {
modules: {
dashboardV2: {
namespaced: true,
state: {
projects: [],
currentTab: "",
selectedFilters: {},
timeFilter: "",
},
actions: {
GET_EXEC_DATA,
GET_EXEC_FILTER_DATA,
},
},
},
};
return new Vuex.Store(mergeWith(storeOptions, overrides, customizer));
};
describe("Loads Main Dashboard", () => {
it("should fetch chart data and filter data", () => {
createWrapper({}, Main, createStore());
expect.assertions(2);
expect(GET_EXEC_DATA).toBeCalled();
expect(GET_EXEC_FILTER_DATA).toBeCalled();
});
it("should call fetch chart data when filter changed", () => {
createWrapper({}, Main, createStore());
EventBus.$emit("filterChange");
expect.assertions(1);
expect(GET_EXEC_DATA).toBeCalledTimes(2);
});
});
My first test is running successfully but my second test is failing because GET_EXEC_DATA is being called 4 times instead of 2 times. Is it because it's being called once in the first test. Then, How do I avoid this?
Actually, I was able to solve this by clearing the mock functions
afterEach(() => {
jest.clearAllMocks();
});

Nuxt + Vuex mapGetters value is always undefined

I'm using VueX with Nuxt.JS so let's suppose the following code in the file store/search.js:
export const state = () => ({
results: null
});
export const mutations = {
setResults(state, { results }) {
state.results = results;
}
};
export const actions = {
startSearch({ commit, dispatch }, { type, filters }) {
commit("setResults", { type, filters });
}
};
export const getters = {
results: state => state.results
};
Now in my component results.vue, under the computed property I have something like this:
<template>
<button #click="handleSearch">Search</button>
<div v-if="results && results.length" class="results" >
<div v-for="item in results" :key="item.id">
{{item}}
</div>
</div>
</template>
<script>
import { mapActions, mapGetters } from "vuex";
data() {
return {
selected_type: null,
filters: null
};
},
methods: {
setType(type) {
this.selected_type = type;
this.handleSearch();
},
setFilters(filters) {
this.filters = filters;
},
handleSearch() {
this.startSearch({ type: this.selected_type, filters: this.filters });
},
...mapActions("search", {
startSearch: "startSearch"
})
},
computed: {
...mapGetters("search", {
results: "results"
})
}
</script>
My question is: why the item in the for loop (in the template section) always return undefined ?
Thank you very much for your answers.
So far, I found it:
in computed should be an array, not an object so:
...mapGetters("search", [
"results"
]
// Now results is populated.

Rerender component after state change vue.js

I am working with NuxtJS and VueJS. I'm having a problem with a component not re-rendering after the state changed.
index.js file
Vue.use(Vuex)
const state = {
productsHome: [],
accessToken: {},
collections: {},
product: {},
cart: {},
}
const getters = {
productForHomepage (state) {
return state.productsHome
},
productForPdp (state) {
return state.product
},
cart (state){
return state.cart
}
}
const actions = {
nuxtServerInit (context) {
//good place to set language
},
GET_HOME(){
api.getHomepageProducts().then(response => {
this.commit('setHomeProducts', response.data)
})
},
GET_PDP(sth){
api.findBySlug(this.app.router.history.current.params.slug).then(response => {
this.commit('setPDPData', response.data)
})
},
ADD_TO_CART(store, id){
api.addToCart(id).then(res => {
store.commit('updateCart', res.data)
})
}
}
const mutations = {
setHomeProducts(state, data){
state.productsHome = data
},
setPDPData(state, data){
state.product = data[0]
},
updateCart(state, data){
for (var optbox of data) {
state.cart[optbox.id] = optbox;
}
// state.cart.set('iteams', 'count', 1)
}
}
const createStore = () => {
return new Vuex.Store({
state,
getters,
mutations,
actions
});
}
export default createStore;
and this is the component
<template>
<div>
<div class="content">
<p>
This is cart
</p>
{{ cart }}
</div>
</div>
</template>
<script>
export default {
data() {
return {
cart: this.$store.state.cart
}
},
watch: {
cart: function(val){
cart = this.$store.state.cart
}
},
methods: {
updateCart: function(){
console.log(this)
}
}
}
</script>
When you do this:
data() {
return {
cart: this.$store.state.cart
}
}
You initilise the data with the value of the cart state, but it won't keep changing when the cart state changes, it's a one time deal, as you can see in this JSFiddle
What you actually want to do is use a computed:
computed: {
cart(){
return this.$store.state.cart
}
}
Now whenever cart state changes in your store, so too will the value of cart in your component.
And here's the JSFiddle for that: https://jsfiddle.net/craig_h_411/zrbk5x6q/

How to add the total + 1 in the text span each time a notification using vue.js 2?

My vue component is like this :
<template>
...
<span v-if="total > 0" class="badge" id="total">{{ total }}</span>
...
</template>
<script>
import { mapGetters } from 'vuex'
export default {
mounted() {
this.initialMount()
},
computed: {
...mapGetters(['total'])
},
methods: {
initialMount() {
Echo.private('App.User.' + window.Laravel.authUser.id).notification((notification) => {
const a = $('#total').text()
const b= parseInt(a) + 1
$('#total').text(b)
})
},
}
}
</script>
My modules is like this :
import { set } from 'vue'
import notification from '../../api/notification'
import * as types from '../mutation-types'
const state = {
total: 0,
}
const getters = {
total: state => state.total
}
const actions = {
getNotificationList ({ commit,state })
{
notification.getList(
data => {
const notifications = data
commit(types.GET_NOTIFICATION,{ notifications });
},
errors => {
console.log(errors)
}
)
}
}
const mutations = {
[types.GET_NOTIFICATION] (state, { notifications }) {
state.total = notifications.length
}
}
export default {
state,
getters,
actions,
mutations
}
===================================================================
I want every notification, the notification number incremented by 1
My above code works, but it still using jquery
I want change it using vue.js
How can I do it?
You have to commit action into the sucess callback of Echo, but first you have to define mutation:
const mutations = {
[types.GET_NOTIFICATION] (state, { notifications }) {
state.total = notifications.length
},
inc (state) {
state.total++
}
}
And then, you can commit action
methods: {
initialMount() {
Echo.private('App.User.' + window.Laravel.authUser.id).notification((notification) => {
// Make sure you have imported store
store.commit('inc')
})
},
}