Vuex Store Unknown Getters with Nuxt - vue.js

I've almoust finished my app in Vuejs and now I decided to "rewrite" everything on Nuxt due to SSR :)
I almoust get everything but there is this getter thing I am not getting.. I can't make it around even though I tried so hard and I am not sure what I am missing.
I am using vuex module so my /store/categories.js looks like:
export const state = () => ({
categories: [
{
name: 'category 1',
id: '1'
subcategories: [
{
name: 'Subcategory 1',
id: '1.1'
}
]
},
{
name: 'category 2',
id: '2',
subcategories: [
{
name: 'Subcategory 2',
id: '1.1'
}
]
}
]
})
export const getters = () => ({
filterCategory() {
return state.categories
},
})
This is a simple getter above and I can't even make this work..
And here is my posts/test.vue page:
<template>
<div>
<p>{{ filter }} </p>
<p>{{ mainCategories }} </p>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'test',
layout: 'test',
data () {
return {
search: '',
}
},
computed: {
...mapGetters({
filter: 'categories/filterCategory'
}),
mainCategories () {
return this.$store.getters['categories/filterpazari']
}
},
}
</script>
What am I missing ?

Getters should be exported as objects not function.
As Bert already mentioned in the comment how to declare getters. But it was trouble some to find the solutions at first glance. So here is the formated answer.
const getters = {
filterCategory: state => state.categories,
}
export default getters
Credit to Bert

Related

How do I work with Bootstrap select options when looping through a non-manual array?

I want to take an object containing an array that I get from a database, and I want the content of this array to appear as an select-option in a Bootstrap's form. I have tried reading Bootstrap's documentation on Form Select, as well as going through some old questions here.
In all cases I get examples of options that have been manually written into the code like this:
<script>
export default {
data() {
return {
selected: 'A',
options: [
{ item: 'A', name: 'Option A' },
{ item: 'B', name: 'Option B' },
{ item: 'D', name: 'Option C', notEnabled: true },
{ item: { d: 1 }, name: 'Option D' }
]
}
}
}
</script>
But this is not what I want. In my case I am fetching some data from a database and storing it in an object that contains an array. That array contains stuff like id, name etc. In my case I just want the name.
<template>
<b-container>
<b-form-select v-model="myDatabaseData">
</b-form-select>
</b-container>
</template>
<script>
export default {
data() {
return {
myDatabaseData: null,
options: []
};
},
created() {
myDB.getData()
.then(result => this.myDatabaseData = result.data)
.catch(error => console.error(error));
},
};
</script>
I have the v-model set up correctly, but what exactly do I do in options?
I thought this was the correct approach:
<template>
<b-container>
<b-form-select v-model="myDatabaseData" :options="options">
</b-form-select>
</b-container>
</template>
<script>
export default {
data() {
return {
myDatabaseData: null,
options: [{ value: this.myDatabaseData.name }]
};
},
created() {
myDB.getData()
.then(result => this.myDatabaseData = result.data)
.catch(error => console.error(error));
},
};
</script>

How to make nested properties reactive in Vue

I hava a component with complex nested props:
<template>
<div>
<a-tree :tree-data="data" :selected-keys="[selectedKey]" #select="onSelect" />
<div>
<a-input v-model="sheet[selectedKey].tableName" />
<ux-grid ref="previewTable">
<ux-table-column v-for="field in sheet[selectedKey].fields"
:key="field.name" :field="field.name">
<a-input slot="header" v-model="field.label" />
</ux-table-column>
</ux-grid>
</div>
</div>
</template>
<script>
export default {
props: {
previewData: { type: Array, default: () => [] }
},
data () {
return {
data: this.previewData,
selectedKey: '0-0-0',
sheet: { 'none': { tableName: null, fields: [] } }
}
},
created () {
this.data.forEach((file, fid) => {
file.sheets.forEach((sheet, sid) => {
this.$set(this.sheet, `0-${fid}-${sid}`, {
tableName: sheet.label,
fields: sheet.fields.map(field => ({ ...field }))
})
})
})
},
mounted () {
this.$refs.previewTable.reloadData(this.data[0].sheets[0].data)
},
methods: {
onSelect ([ key ], { node }) {
if (key !== undefined && 'fields' in node.dataRef) {
this.selectedKey = key
this.$refs.previewTable.reloadData(node.dataRef.data)
} else {
this.selectedKey = 'none'
this.$refs.previewTable.reloadData()
}
}
}
}
</script>
And previewData props is something like:
{
name: "example.xlsx",
filename: "80b8519f-f7f1-4524-9d63-a8b6c92152b8.xlsx",
sheets: [{
name: "example",
label: "example",
fields:[
{ label: "col1", name: "col1", type: "NUMBER" },
{ label: "col2", name: "col2", type: "STRING" }
]
}]
}
</script>
This component allows user to edit the label properties. I have to make Object sheet reactive to user input, and I tried $set and Object.assign, it works for sheets.label but fields[].label is still not reactive.
I wish to know what would be the declarative (and optimal) solution for it
You might need a watcher or computed property in React for previewData to be changed.

How to make ref in component link to component data?

I am using https://github.com/r3code/vue-vis-network
I am getting error: Cannot read property 'hasChildNodes' of undefined
My component code is:
<template>
<el-container >
<el-main>
<network ref="network" :nodes="nodes" :edges="edges" :options="options"> </network>
<button #click="get_data">GetData</button>
</el-main>
</el-container>
</template>
<script>
export default {
components: { Notification },
data () {
return {
nodes: [],
edges: [],
options: []
}
},
methods:
{
get_data()
{
axios.get(base_url + '/graph')
.then((response) => {
this.nodes = response.data.nodes;
this.edges = response.data.edges;
}).catch((error) => { console.log(error); });
}
},
}
</script>
index.js:
Vue.component('network', new vueVisNetwork.Network);
I suppose that there is problem that it's attempt to access to data placed in #app. How I can say him that he should use data declared in component?
Actual data example:
nodes : [
{id: 1, label: 'Fooo"', group: "company"},
{id: 2, label: 'Baaar', group: "owner"},
],
edges : [
{from: 1, to: 2},
],
possible related issue https://github.com/crubier/react-graph-vis/issues/57

VueJs: Form handling with Vuex and inputs generated with an API

Here's an example of a component:
<script>
export default {
name: 'my-form',
computed: {
myModules() {
return this.$store.state.myModules;
}
}
</script>
<template>
<form>
<p v-for="module in myModules">
<input type="checkbox" :value="module.id" />
<label>module.name</label>
</p>
<button type="submit">Submit</button>
</form>
</template>
The associated store:
state: {
myModules: []
},
mutations: {
setModules(state, modules) {
state.myModules = modules;
}
},
actions: {
getModules({commit}) {
return axios.get('modules')
.then((response) => {
commit('setModules', response.data.modules);
});
}
}
And finally, an example of return of the API "getModules":
modules : [
{
id: 1,
name: 'Module 1',
isActive: false
},
{
id: 2,
name: 'Module 2',
isActive: false
},
{
id: 3,
name: 'Module 3',
isActive: false
}
]
My question: what's the best way to change the "isActive" property of each module to "true" when I check the checkbox corresponding to the associated module, directly in the store?
I know that Vuex's documentation recommends to use "Two-way Computed Property" to manage the forms, but here I don't know the number of modules that the API can potentially return, and I don't know their name.
Thank you in advance!
This is a little bit wicked approach, but it works. You can create an accessor object for every item you access in a loop:
const store = new Vuex.Store({
mutations: {
setActive (state, {index, value}) {
state.modules[index].isActive = value
}
},
state: {
modules : [
{
id: 1,
name: 'Module 1',
isActive: false
},
{
id: 2,
name: 'Module 2',
isActive: false
},
{
id: 3,
name: 'Module 3',
isActive: false
}
]
}
});
const app = new Vue({
el: '#target',
store,
methods: {
model (id) {
const store = this.$store;
// here i return an object with value property that is bound to
// specific module and - thanks to Vue - retains reactivity
return Object.defineProperty({}, 'value', {
get () {
return store.state.modules[id].isActive
},
set (value) {
store.commit('setActive', {index: id, value});
}
});
}
}
})
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/vuex/dist/vuex.min.js"></script>
<div id="target">
<div v-for="(item, id) in $store.state.modules">
Module #{{ item.id }} state: {{ item.isActive }}
</div>
<div v-for="(item, id) in $store.state.modules">
<label>
Module #{{ item.id }}
<input type="checkbox" v-model="model(id).value"/>
</label>
</div>
</div>
This is still quite a messy approach, but at least you don't have to commit mutations directly in template. With a little help of Vue.set() you can use this approach even to overcome standard reactivity caveats.
I have an alternative solution for you. You could make a child component for the checkboxes to clean up the code a bit.
UPD: I just realised that everything that I and #etki proposed is an overkill. I left the old version of my code below in case you still want to take a look. Here is a new one:
const modules = [{
id: 1,
name: 'Module 1',
isActive: true,
},
{
id: 2,
name: 'Module 2',
isActive: false,
},
{
id: 3,
name: 'Module 3',
isActive: false,
},
];
const store = new Vuex.Store({
state: {
myModules: [],
},
mutations: {
SET_MODULES(state, modules) {
state.myModules = modules;
},
TOGGLE_MODULE(state, id) {
state.myModules.some((el) => {
if (el.id === id) {
el.isActive = !el.isActive;
return true;
}
})
}
},
actions: {
getModules({
commit
}) {
return new Promise((fulfill) => {
setTimeout(() => {
commit('SET_MODULES', modules);
fulfill(modules);
}, 500)
});
}
}
});
const app = new Vue({
el: "#app",
store,
data: {},
methods: {
toggle(id) {
console.log(id);
this.$store.commit('TOGGLE_MODULE', id);
}
},
computed: {
myModules() {
return this.$store.state.myModules;
},
output() {
return JSON.stringify(this.myModules, null, 2);
},
},
mounted() {
this.$store.dispatch('getModules').then(() => console.log(this.myModules));
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.js"></script>
<script src="https://unpkg.com/vue"></script>
<div id="app">
<form>
<div v-for="data in myModules">
<label :for="data.id">{{ data.name }}: {{data.isActive}}</label>
<input type="checkbox" :id="data.id" :name="'checkbox-' + data.id" :checked="data.isActive" #change="toggle(data.id)">
</div>
</form>
<h3>Vuex state:</h3>
<pre v-text="output"></pre>
</div>
As you can see above you could just call a function on input change and pass an id as a parameter to a method that fires vuex action.
The old version of my code.
A new one on jsfiddle

How to create dynamic links based on an API request

I am completely new to both vue.js and Javascript. How do I dynamically create nav links from an Axios request?
I am wanting to follow what's being done in the item section which is currently static information, but i want to dynamically return links based on whats returned in the json request.
import * as types from '../../mutation-types'
import lazyLoading from './lazyLoading'
import charts from './charts'
// gathering items from API
const url = 'http://localhost:8080/items/'
data: {
items: []
},
mounted() {
axios.get(url).then(response => {
this.results = items.data
})
}
// Sidebar links are statically created here
const state = {
items: [
{
name: 'Dashboard',
path: '/dashboard',
meta: {
icon: 'fa-tachometer',
link: 'dashboard/index.vue'
},
component: lazyLoading('dashboard', true)
},
{
name: 'Axios',
path: '/axiosDemo',
meta: {
auth: true,
icon: 'fa-rocket',
link: 'axios/index.vue'
},
component: lazyLoading('axios', true)
},
charts,
]
}
const mutations = {
[types.EXPAND_MENU] (state, menuItem) {
if (menuItem.index > -1) {
if (state.items[menuItem.index] && state.items[menuItem.index].meta) {
state.items[menuItem.index].meta.expanded = menuItem.expanded
}
} else if (menuItem.item && 'expanded' in menuItem.item.meta) {
menuItem.item.meta.expanded = menuItem.expanded
}
}
}
export default {
state,
mutations
}
I think what I am wanting to do is something like this (python example):
items:
for i in items_payload:
{
name: i.name,
path: i.url,
meta: {
icon: 'fa-tachometer',
link: i.name+'/index.vue'
},
},
How do I best accomplish this in vue.js? Any help would be appreciated. Thanks.
If you are making the api request from a component, you can create a list of links like follows:
// for demo purposes, let's say links are returned as an array of objects
[
{
href: '/path/to/page1',
linkText: 'Page 1'
},
{
href: '/path/to/page2',
linkText: 'Page 2'
}
]
// MyComponent.vue
<template>
<div class="sidebar">
<ul>
<li v-for="(item, index) in items" :key="index">
<a :href="item.href">{{ item.linkText }}</a>
</li>
</ul>
</div>
</template>
export default {
data () {
return {
links: []
}
},
mounted() {
axios.get(url).then(response => {
this.links = items.data
})
}
}