I'm building a vue2 component, with a vuex store object. The component looks like this:
<template>
<ul id="display">
<li v-for="item in sourceData()">
{{item.id}}
</li>
</ul>
</template>
<script>
export default {
mounted: function () {
console.log('mounted')
},
computed: {
sourceData: function() {
return this.$store.getters.visibleSource
}
}
}
</script>
The store is populated via an ajax call at the beginning of the process, in the main javascript entry:
new Vue({
store,
el: '#app',
mounted: function() {
this.$http.get('/map/' + this.source_key + '/' + this.destination_key)
.then(function (response) {
store.commit('populate', response.data)
})
.catch(function (error) {
console.dir(error);
});
}
});
I'm not seeing any errors, and when I use the Vue devtools explorer I can see that my component's sourceData attribute is populated with hundreds of items. I'd expect that once this data is populated, I'd see a bunch of li rows with item.id in them appear on the page.
But despite no errors and apparently good data in the component, I am not seeing the template render anything.
Do I need to use some sort of callback to fire the component after the vuex store is populated?
EDIT: adding store code:
import Vue from 'vue';
import Vuex from 'vuex';
import { getSource, getDestination } from './getters'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
field_source: [],
field_destination: []
},
getters: {
visibleSource: state => {
// this just does some formatting
return getSource(state.field_source)
},
visibleDestination: state => {
return getDestination(state.field_destination)
}
},
mutations: {
populate(state, data) {
state.field_source = data.source
state.field_destination = data.destination
}
}
})
EDIT2: Maybe it's not a problem with the v-for-- I don't see anything from the template being rendered, not even the main ul tag, which I'd expect to see (empty) even if there was a problem further in the script.
sourceData is a computed property, not a method. You don't need to invoke it. Don't use it like v-for="item in sourceData()", use it like v-for="item in sourceData".
Other than that, on your 'populate' mutation you are overwritting the observed/reactive objects.
Either use Vue.set():
mutations: {
populate(state, data) {
// was state.field_source = data.source
Vue.set(state, 'field_source', data.source);
// was state.field_destination = data.destination
Vue.set(state, 'field_destination', data.destination);
}
}
Or push all elements to the existing, observed/reactive, arrays:
mutations: {
populate(state, data) {
// was state.field_source = data.source
state.field_source.push(...data.source);
// was state.field_destination = data.destination
state.field_destination.push(...data.destination);
}
}
Related
This is regarding Vuex and passing of data from one component to another. I want the Module variable within CoreMods.vue to be passed to the ExternalWebpage.vue. Or rather, be watched in the external webpage for changes.
My store.js looks like this:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.store({
state:{
CoreModule: ""
},
mutations:{
update: (state, n) => {
state.CoreModule = n;
}
},
getters:{
updated: state =>{
return state.CoreModule;
}
},
actions:{
async createChange({ commit }, n) {
commit("update", n);
}
}
});
I have a CoreMods.vue
methods:{
checkModule() {
if(!this.completed_cm.includes(this.Module)) {
if (this.core.includes(this.Module)) {
this.completed_cm.push(this.Module);
this.$store.dispatch('createChange',this.Module);
}
},
An ExternalWebpage.vue
watch:{
'$store.state.CoreModule': function(){
var cm = this.$store.getters.updated;
if(this.CompletedCore.indexOf(cm) == -1){
this.CompletedCore.push(cm);
}
}
}
I can't make use of props by importing one component in another. This is because:
1) I do not want the entire component placed within the parent component.
2) CoreMod is a component on the home page. Upon clicking a link on the home page, it leads to ExternalWebpage. (This has already been implemented using router)
Currently this code isn't working. Can someone help find a solution/alternative. Additionally, how should I add this part to the main.js?
Thanks!!!
possible solution is to return vuex store value from computed and watch the computed value.
computed: {
coreModule () {
return this.$store.state.CoreModule;
}
},
watch:{
'coreModule': function(){
var cm = this.$store.getters.updated;
if(this.CompletedCore.indexOf(cm) == -1){
this.CompletedCore.push(cm);
}
}
}
I can't realize how to get it works.
I'm trying to load a propertie (productos) on my data() which has to catch the value from a state.
My component:
data () {
return {
productos: this.$store.state.mStock.productos
}
},
created() {
this.$store.dispatch('fetchProductos')
}
At this point i think it's okay, when i load my component i dispatch my action to load the state on the store.
I think the problem is that the way i fill the state is ASYNC
Store:
import StockService from '#/services/StockService'
export const moduleStock = {
strict: false,
state: {
productos: []
},
mutations: {
setProductos (state, payload) {
state.productos = payload.productos
}
},
actions: {
async fetchProductos ({commit}, payload) {
const resp = await (StockService.getProductos())
var productos = resp.data
commit('setProductos', {productos: productos})
}
}
}
When i load my component, the propertie "productos" on the data() is null, however if i see the 'state.productos' from the Vuex devtools, it has the data!
I'm messed up.
The data() method is only run once.
This might seem to work if when the component and the vue store use the same object instance, but doesn't work in this case because a new array instance is assigned in the store while the component stil has the previous instance (the empty array)
Use computed properties. I recommend using the mapState() helper:
computed: mapState({
productos: state => state.mStock.productos
})
without mapState you'd write:
computed: {
productos() {
return this.$store.state.mStock.productos
}
}
I have a masterData.js file that is a store for my master data, in short the file reads my mongo db data & sends it to other project components. I created a function to export the string in the masterData.js file as:
/ ***************************** MUTATIONS
const mutations = {
exportColumns (payload) {
Object.keys(payload[0]).map(x => { return x; });
}
}
Where payload will store all the rows and payload[0] holds the value of column header names. The output of this chunk of code is like this:
["_id","businessAreaName","businessAreaDisplay","councilDisplay","councilID"]
I want to transfer the values to masterData.vue file. My code on masterData.Vue is:
importColumns ()
{
let payload = {
vm: this,
mutation: 'masterData/exportColumns'
};
}
What else should I add to to check whether the column names are received or not?
If you're trying to access the data in your store from within a component, then you'll want to either just map the state to the component or map a getter to the component. Mutations are used by components (or by actions) to modify the state of the store. So instead you would do something like...
//masterData.js
//assuming this gets rolled up as a module called masterdata to the primary store
//store for payload state
const state = {
payload: null,
}
//allows payload to be set -- not sure how you are retrieving the payload but you can use this to store it however you get it
const mutations = {
setPayload (state, payload) {
state.payload = payload
}
}
//get just the columns
const getters = {
getColumns (state) {
Object.keys(state.payload[0]).map(x => { return x; })
}
}
export default {
state,
mutations,
getters,
}
Then
//masterData.vue
<template>
//...
</template>
<script>
import { mapGetters, mapState } from 'vuex'
export default {
computed: {
//I believe getting state from a store module requires a function like this
...mapState({
payload: function(state) {
return state.masterdata.payload
},
}),
//I think for getters you can just reference the method and it will find it
...mapGetters([
'getColumns',
])
},
}
</script>
This is how you import stuff in a single file component.
<template>
<!-- html stuff -->
</template>
<script>
import Mutations from 'yourModule.js'
export default {
name: 'YourComponent',
props: {},
data(){
return {
foo: 'foo'
}
},
methods{
mymethod() {
Mutations.exportColumn(this.foo);
},
}
}
</script>
I'm new to vue, so I'm probably making a rookie error.
I have a root vue element - raptor.js:
const Component = {
el: '#app',
store,
data: {
productList: store.state.productlist
},
beforeCreate: function () {
return store.dispatch('getProductList', 'getTrendingBrands');
},
updated: function (){
console.log(111);
startSlider();
}
};
const vm = new Vue(Component);
Using this template
<div class="grid-module-single popular-products" id="app">
<div class="row">
<div class="popular-items-slick col-xs-12">
<div v-for="product in productList">
...
</div>
</div>
</div>
My store is very simple store/index.js:
import Vue from 'vue';
import Vuex from 'vuex';
import model from '../../utilities/model';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
productlist: []
},
mutations: {
setProductList(state, data) {
state.productlist = data;
}
},
actions: {
getProductList({ commit }, action) {
return model.products().then(data => commit('setProductList', data));
}
}
});
In my vuex devtool, I can see, that the store is being updated
https://www.screencast.com/t/UGbw7JyHS3
but my component is not being updated:
https://www.screencast.com/t/KhXQrePEd
Question:
I can see from the devtools, that my code is working. The store is being updated with data. My component is not being updated,however. I thought it was enough just to add this in the data property on the component:
data: {
productList: store.state.productlist
}
but apparently the data object doesn't seem to be automatically synced with the store. So either I'm doing a complete vue no-no somewhere, or I need to tweak the code a bit. Anyway can anyone help me in the right direction.
Thanks a lot.
UPDATE
Figured it out myself. Just had to replace the components data part with a computed method:
data:
data: {
productList: store.state.productlist
}
and replace it with.
computed: {
productList () {
return store.state.productlist;
}
},
data only work once on component before render, so you can use computed instead.
like above answer, or you can use mapstate
import {mapState} from 'vuex'
...
computed: mapState({
productList: state => state.productList
})
First - use getter to do this mapGetters, also you need to watch this property somehow, you can set store subscription or just with watch method trough component.
this.$store.subscribe((mutation, state) => {
if (mutation.type === 'UPDATE_DATA') {
...
}
}
You are calling the store into the productList data property in the wrong way.
You can try it:
data: {
productList: $store.state.productlist
}
Otherwise you have to import store in each component that are using the store.
I'm trying to design a store to manage the events of my Vuex application. This far, I have the following.
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const state = { dataRows: [], activeDataRow: {} };
const mutations = {
UPDATE_DATA(state, data) { state.dataRows = data; state.activeDataRow = {}; },
};
export default new Vuex.Store({ state, mutations });
I'm going to have a number of list items that are supposed to change the value of the data in the store when clicked. The design of the root component App and the menu bar Navigation is as follows (there will be a bunch of actions in the end so I've collected them in the file actions.js).
<template>
<div id="app">
<navigation></navigation>
</div>
</template>
<script>
import navigation from "./navigation.vue"
export default { components: { navigation } }
</script>
<template>
<div id="nav-bar">
<ul>
<li onclick="console.log('Clickaroo... ');">Plain JS</li>
<li #click="updateData">Action Vuex</li>
</ul>
</div>
</template>
<script>
import { updateData } from "../vuex_app/actions";
export default {
vuex: {
getters: { activeDataRow: state => state.activeDataRow },
actions: { updateData }
}
}
</script>
Clicking on the first list item shows the output in the console. However, when clicking on the second one, there's nothing happening, so I'm pretty sure that the event isn't dispatched at all. I also see following error when the page's being rendered:
Property or method "updateData" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
I'm very new to Vuex so I'm only speculating. Do I need to put in reference to the updateData action in the store, alongside with state and mutations? How do I do that? What/where's the "data option" that the error message talks about? Isn't it my components state and it's properties?
Why the error
You are getting the error, because when you have <li #click="updateData"> in the template, it looks for a method updateData in the vue component which it does not find, so it throws the error. To resolve this, you need to add corresponding methods in the vue component like following:
<script>
import { updateData } from "../vuex_app/actions";
export default {
vuex: {
getters: { activeDataRow: state => state.activeDataRow },
actions: { updateData }
},
methods:{
updateData: () => this.$store.dispatch("updateData")
}
}
</script>
What this.$store.dispatch("updateData") is doing is calling your vuex actions as documented here.
What/where's the "data option"
You don't have any data properties defined, data properties for a vue component can be used, if you want to use that only in that component. If you have data which needs to be accessed across multiple components, you can use vuex state as I believe you are doing.
Following is the way to have data properties for a vue component:
<script>
import { updateData } from "../vuex_app/actions";
export default {
date: {
return {
data1 : 'data 1',
data2 : {
nesteddata: 'data 2'
}
}
}
vuex: {
getters: { activeDataRow: state => state.activeDataRow },
actions: { updateData }
},
methods:{
updateData: () => this.$store.dispatch("updateData")
}
}
</script>
You can use these data properties in the views, have computed properies based on it, or create watchers on it and many more.