vue2 disable input with multiple components - javascript

Vue 2 - disable input - multiple components
Hi all,
I struggle to solve a problem, where I want to disable other input fields, once the first char has been entered in another input field.
I've been trying to solve this with $emit, #focus, and other solutions and I'm still stuck. I was also not able to utilize the answers to be found here.
Snippet:
const Autocomplete = {
name: "autocomplete",
props: {
items: {
type: Array,
required: false,
default: () => ['test']
},
isAsync: {
type: Boolean,
required: false,
default: false
},
formLock: {
type: Boolean,
},
formId: {
type: String,
}
},
data() {
return {
isOpen: false,
results: [],
search: "",
isLoading: false,
arrowCounter: 0,
};
},
methods: {
onChange() {
// Let's warn the parent that a change was made
this.$emit("input", this.search);
// Is the data given by an outside ajax request?
if (this.isAsync) {
this.isLoading = true;
} else {
// Let's search our flat array
this.filterResults();
this.isOpen = true;
}
if (this.search.length === 0) {
this.isOpen = false;
}
console.log(this.search.length);
},
disableOther() {
var searchForms = document.getElementsByClassName('searchForm');
for (i = 0; i < searchForms.length; i++) {
}
console.log(searchForms.length);
},
filterResults() {
// first uncapitalize all the things
this.results = this.items.filter(item => {
return item.toLowerCase().indexOf(this.search.toLowerCase()) > -1;
});
},
setResult(result) {
this.search = result;
this.isOpen = false;
},
onArrowDown(evt) {
if (this.arrowCounter < this.results.length) {
this.arrowCounter = this.arrowCounter + 1;
}
},
onArrowUp() {
if (this.arrowCounter > 0) {
this.arrowCounter = this.arrowCounter - 1;
}
},
onEnter() {
this.search = this.results[this.arrowCounter];
this.isOpen = false;
this.arrowCounter = -1;
},
handleClickOutside(evt) {
if (!this.$el.contains(evt.target)) {
this.isOpen = false;
this.arrowCounter = -1;
}
}
},
mounted() {
document.addEventListener("click", this.handleClickOutside);
},
destroyed() {
document.removeEventListener("click", this.handleClickOutside);
},
template: `
<div>
<input type="text" #input="onChange" class="searchForm" v-model="search" #keyup.down="onArrowDown" #keyup.up="onArrowUp" #keyup.enter="onEnter" v-bind:disabled="formLock" #focus="disableOther" />
<ul id="autocomplete-results" v-show="isOpen" class="autocomplete-results">
<li class="loading" v-if="isLoading">
Loading results...
</li>
<li v-else v-for="(result, i) in results" :key="i" #click="setResult(result)" class="autocomplete-result" :class="{ 'is-active': i === arrowCounter }">
{{ result }}
</li>
</ul>
</div>
`,
};
new Vue({
el: "#productSearchApp",
name: "productSearchApp",
data() {
return {
productName: [],
productCatalog: [],
lock: false,
searchName: "searchForm",
searchCatalog: "searchCatalog"
}
},
mounted() {
fetch("http://cormay.314-work.pl/wp-json/wp/v2/product")
.then(response => response.json())
.then((data) => {
for (i = 0; i < data.length; i++) {
this.productName.push(data[i].title.rendered);
};
for (i = 0; i < data.length; i++) {
this.productCatalog.push(data[i].product_catalog);
};
})
},
components: {
autocomplete: Autocomplete,
},
methods: {
updateLock(updateLock) {
this.lock = updateLock;
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="productSearchApp">
<autocomplete :items="productName" :form-id="searchName"></autocomplete>
<autocomplete :items="productCatalog" :form-id="searchCatalog"></autocomplete>
</div>
Thanks!

You could try something like this.
You'll notice that I'm passing the name of model back and forth a bit, which may seem like a nuisance to manage, but if you were to configure it as part of a v-for loop, it would make it easy to manage.
Vue.component('custom-input', {
props: ['value', 'disabled'],
template: `
<input
:disabled="disabled"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
new Vue({
el:'#app',
data:{
first: null,
second: null,
active: null
},
methods: {
onChange(e, model){
this.active = null
if (e.length > 0) {
this.active = model
}
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.js"></script>
<div id="app">
<custom-input v-model="first" :disabled="active !== null && active !== 'first'" #input="onChange($event, 'first')">Foo</custom-input>
<custom-input v-model="second" :disabled="active !== null && active !== 'second'" #input="onChange($event, 'second')">Bar</custom-input>
</div>

Maybe you could use a state manager like VueX or Instance Properties
... like:
// global state
Vue.prototype.$state = new Vue({
data: { 
active: null
}
})
// input component
Vue.component('vue-input', {
props: ['value'],
template: `
<label>
<slot></slot>
<input v-model="model" :disabled="disabled" />
</label>
`,
data() {
return {
// avoid mutation
model: this.value
}
},
beforeDestroy() {
// reset state in case we remove the active component
if (this.$state.active === this) this.$state.active = null;
},
watch: {
model(value) {
// set active to 'this' if value length > 0 or null active value
this.$state.active = value.length ? this : null;
}
},
computed: {
disabled() {
// disable if active is not null and not equal to 'this'
return this.$state.active && this.$state.active !== this;
}
}
})
// app instance
new Vue({
el: '#app',
data: {
foo: null,
bar: null
}
})
label {
font: caption;
display: flex;
justify-content: space-between;
width: 160px;
margin-bottom: 10px;
}
input:disabled {
background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.js"></script>
<div id="app">
<vue-input v-model="foo">Foo</vue-input>
<vue-input v-model="bar">Bar</vue-input>
</div>

Related

how do i use vuex mutation payload object properly

I have 2 inputs in which i provide value to search whether its name of the company, position (1st input) or location (2nd input). It works with one argument provided into foundJobs mutation and then into action. But when payload has an object everything is undefined and array is empty. What am i doing wrong?
component:
<script setup>
import IconSearch from "../Icons/icon-search.vue";
import IconLocation from "../Icons/icon-location.vue";
import { ref } from "vue";
import { useStore } from "vuex";
const store = useStore();
const nameFilter = ref("");
const locationFilter = ref("");
</script>
<template>
<div class="header-filter">
<div class="header-filter__search">
<IconSearch />
<input
type="text"
placeholder="Filter by title, companies, expertise…"
ref="nameFilter"
/>
</div>
<div class="header-filter__location">
<IconLocation />
<input
type="text"
placeholder="Filter by location…"
ref="locationFilter"
/>
</div>
<div class="header-filter__fulltime">
<input type="checkbox" />
<p>Full Time Only</p>
<button
type="button"
#click="
store.dispatch('foundJobs', {
nameFilter: nameFilter.value,
locationFilter: locationFilter.value,
})
"
>
Search
</button>
</div>
</div>
</template>
vuex: (not working)
import { createStore } from "vuex";
const store = createStore({
state() {
return {
jobs: [],
filteredJobs: [],
};
},
mutations: {
setJobs(state, jobs) {
state.jobs = jobs;
},
foundJobs(state, { nameInputValue, locationInputValue }) {
let copiedJobsArr = [...state.jobs];
if (nameInputValue !== "") {
copiedJobsArr = copiedJobsArr.filter(
(job) =>
job.company === nameInputValue || job.position === nameInputValue
);
}
if (locationInputValue !== "") {
copiedJobsArr = copiedJobsArr.filter(
(job) => job.location === locationInputValue
);
}
console.log(locationInputValue); // undefined
state.filteredJobs = copiedJobsArr;
console.log(state.filteredJobs); //empty array
},
},
actions: {
foundJobs(context, { nameInputValue, locationInputValue }) {
context.commit("foundJobs", { nameInputValue, locationInputValue });
},
loadJobs(context) {
return fetch("./data.json")
.then((response) => {
return response.json();
})
.then((data) => {
const transformedData = data.map((job) => {
return {
id: job.id,
company: job.company,
logo: job.logo,
logoBackground: job.logoBackground,
position: job.position,
postedAt: job.postedAt,
contract: job.contract,
location: job.location,
website: job.website,
apply: job.apply,
description: job.description,
reqContent: job.requirements.content,
reqItems: job.requirements.items,
roleContent: job.role.content,
roleItems: job.role.items,
};
});
context.commit("setJobs", transformedData);
});
},
},
getters: {
jobs(state) {
return state.jobs;
},
filteredJobOffers(state) {
return state.filteredJobs;
},
},
});
export default store;
vuex (working) - here i also provide one argument into action assigned to a button (in a component file)
import { createStore } from "vuex";
const store = createStore({
state() {
return {
jobs: [],
filteredJobs: [],
};
},
mutations: {
setJobs(state, jobs) {
state.jobs = jobs;
},
foundJobs(state, nameInputValue) {
let copiedJobsArr = [...state.jobs];
if (nameInputValue !== "") {
copiedJobsArr = copiedJobsArr.filter(
(job) =>
job.company === nameInputValue || job.position === nameInputValue
);
}
console.log(nameInputValue);
state.filteredJobs = copiedJobsArr;
console.log(state.filteredJobs);
},
},
actions: {
foundJobs(context, nameInputValue) {
context.commit("foundJobs", nameInputValue);
},
loadJobs(context) {
return fetch("./data.json")
.then((response) => {
return response.json();
})
.then((data) => {
const transformedData = data.map((job) => {
return {
id: job.id,
company: job.company,
logo: job.logo,
logoBackground: job.logoBackground,
position: job.position,
postedAt: job.postedAt,
contract: job.contract,
location: job.location,
website: job.website,
apply: job.apply,
description: job.description,
reqContent: job.requirements.content,
reqItems: job.requirements.items,
roleContent: job.role.content,
roleItems: job.role.items,
};
});
context.commit("setJobs", transformedData);
});
},
},
getters: {
jobs(state) {
return state.jobs;
},
filteredJobOffers(state) {
return state.filteredJobs;
},
},
});
export default store;
store.dispatch('foundJobs', {
nameFilter: nameFilter.value,
locationFilter: locationFilter.value,
})
You are sending data like this and trying to get on the wrong way
foundJobs(state, { nameInputValue, locationInputValue })
you can receive data this way:
foundJobs(state, { nameFilter, locationFilter})

Infinite loop while changing disabled attribute with a prop. (VueJs)

I am trying to change the disabled attribute of a button with a prop but when I change prop's value while instance is created it throws in an infinite loop.
This is parent component.
This is Button component.
This is the error when I change value of disabled prop from parent component.
Here's the full code of button component -
<template>
<button
:class="[checkType(buttonType), checkIcon(icon)]"
:disabled="disabled"
#click="$emit('clicked'); buttonClicked();"
>
<p v-if="!icon && buttonType != 'iconOnly' && !buttonClickedBool">{{ buttonText }}</p>
<div v-if="buttonClickedBool && loading" class="loader"> </div>
</button>
</template>
<script>
export default {
props: {
text: {
type: String,
default: null
},
icon: {
type: String,
default: null
},
buttonType: {
type: String,
required: true,
default: "primary"
},
loading: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false,
}
},
data() {
return {
buttonClickedBool: false,
buttonText: "",
}
},
methods: {
checkType(buttonType) {
this.buttonText = this.text;
let buttonClass = "";
if (buttonType === "primary") {
buttonClass = "primary";
this.buttonText = this.text.toUpperCase();
} else if (buttonType === "secondary") {
buttonClass = "secondary";
} else if (buttonType === "textOnly") {
buttonClass = "textOnly";
} else if (buttonType === "iconOnly") {
buttonClass = "iconOnly";
}
return buttonClass;
},
buttonClicked() {
if(this.loading) {
this.buttonClickedBool = true;
}
console.log("check")
},
checkIcon(icon) {
let iconClass = "";
switch (icon) {
case 'like':
iconClass = "like";
return iconClass;
case 'dislike':
iconClass = "dislike";
return iconClass;
default:
iconClass = '';
}
},
},
};
</script>

Component in use in multiple places all are being updated at the same time

I am new still to Vue I have a component that is used to accept and display files when they are dragged & Dropped onto the component. According to the documentation if I have my attributes in a data function then I can use this component in multiple places and have them update independently of the other component.
This is not the case when I test the component in my add screen the component in the view screen is still updated with the same data. I cannot figure out why this is happening so was wondering if I could get some fresh eyes
to take a look or point me in the right direction on how I can resolve this issue.
this is my ImageViewer code
<template>
<div id="image-panel"
#dragenter="allowDrag"
#mouseenter="toggleSingleViewActionbar('show')"
#mouseleave="toggleSingleViewActionbar('hide')">
<div id="dragDropOverlay" class="drop display-inline align-center"
:class="[isOverlay ? 'overlay' : '']"
#dragleave="retainOverlay"
#dragover.prevent
#drop="onDrop">
<div class="overlay-text">Drag files here</div>
</div>
<single-view v-if="display === 'single'"
:file-list-length="this.fileList.length === 0 ? 0 : this.fileList.length - 1"
:current-position="currentPosition"
:display-type="displayType"
v-on:addToFileList="updateFileList"/>
<grid-view v-else
:file-list-length="this.fileList.length === 0 ? 0 : this.fileList.length - 1"
:current-position="currentPosition"/>
<app-image-section-single-view
v-if="display === 'single'"
:fileList="fileList"
:currentPosition="currentPosition"
:is-overlay="isOverlay"
/>
<app-image-section-grid-view v-else :file-list="fileList"/>
<snack-bar v-if="deleteConfirmation" message="Are you sure you want to delete this file?"></snack-bar>
</div>
</template>
<script>
import ImageSectionSingleView from './ImageSectionSingleView'
import ImageSectionGridView from './ImageSectionGridView.vue'
import { eventBus } from '#/main'
import Singleview from '../Actionbar/Singleview'
import Gridview from '../Actionbar/Gridview'
import SnackBar from '../SnackBar/SnackBar'
import { states } from '../enums/enums'
export default {
data () {
return {
fileList: [],
currentPosition: 0,
display: 'single',
displayType: '',
isOverlay: false,
deleteConfirmation: false
}
},
watch: {
fileList () {
eventBus.$emit('updateFileList', this.fileList)
}
},
methods: {
onDrop (e) {
e.stopPropagation()
e.preventDefault()
if (!this.isOverlay) return
this.updateFileList(e.dataTransfer.files)
this.isOverlay = false
},
allowDrag () {
this.isOverlay = this.$store.getters.appState !== states.view
},
retainOverlay (event) {
if (!this.isOverlay) return
this.isOverlay = !!event.relatedTarget.closest('#dragDropOverlay')
},
getFileList () {
return this.$store.getters.getFileList
},
updateFileList (files) {
this.fileList.push.apply(this.fileList, (
[...files].map((f) => ({
name: f.name,
data: URL.createObjectURL(f),
type: f.type
}))))
this.currentPosition = this.fileList.length - 1
},
getCurrentPosition () {
return this.$store.getters.getCurrentPosition
},
// updateCurrentPosition (position) {
// this.$store.commit('updateCurrentPosition', position)
// },
toggleSingleViewActionbar (action = '') {
this.displayType = action
},
deleteImage (index = -1) {
if (index === -1) {
index = this.currentPosition
}
this.fileList.splice(index, 1)
if (index < this.fileList.length) return
if (this.currentPosition > 0) {
this.currentPosition--
}
},
deleteSelected (indexes) {
for (let i = 0; i < indexes.length; i++) {
this.deleteImage(indexes[i])
}
this.fileList.map((file) => {
file.isVisible = false
})
}
},
created () {
this.fileList = this.getFileList()
this.currentPosition = this.getCurrentPosition()
eventBus.$on('deleteSelectedFiles', (indexes) => {
this.deleteSelected(indexes.sort().reverse())
if (this.fileList.length === 0) {
this.currentPosition = 0
this.display = 'single'
}
})
eventBus.$on('setSelectedFiles', (state) => {
this.fileList.map((file) => {
file.isVisible = state
})
})
eventBus.$on('moveToNextFile', (positionNext) => {
this.currentPosition = positionNext++
})
eventBus.$on('moveToPreviousFile', (positionPrevious) => {
this.currentPosition = positionPrevious--
})
eventBus.$on('confirmDelete', () => {
eventBus.$emit('singleDeleteConfirmation', () => {
})
})
eventBus.$on('confirmationYes', () => {
this.deleteImage()
eventBus.$emit('singleDeleteSnackbarClose')
})
eventBus.$on('confirmationNo', () => {
eventBus.$emit('singleDeleteSnackbarClose')
})
eventBus.$on('switchView', (value) => {
this.display = value
})
eventBus.$on('singleDeleteConfirmation', () => {
this.deleteConfirmation = !this.deleteConfirmation
})
eventBus.$on('singleDeleteSnackbarClose', () => {
this.deleteConfirmation = false
})
},
components: {
appImageSectionSingleView: ImageSectionSingleView,
appImageSectionGridView: ImageSectionGridView,
singleView: Singleview,
gridView: Gridview,
SnackBar: SnackBar
}
}
</script>
and this is where the image/file is displayed
<template>
<div class="display-inline">
<img #dragenter="isOverlay=true" v-if="fileList.length > 0" :src="fileList[currentPosition].data" class="img" />
<img v-else src="../../../src/assets/logo.png" class="img" />
</div>
</template>
<script>
export default {
props: {
fileList: Array,
currentPosition: Number,
fileListLength: Number,
isOverlay: Boolean
}
}
</script>
How can I get it so that It is independently displaying in each of my sections where the component is called ?
Initially the data properties are all independent but then you're assigning this.fileList = this.getFileList(), which is grabbing an array from the store. All components will be sharing that same array in their fileList property. - Comment by skirtle

Vue, how to pass function in props in JSX render?

My components looks like:
App.jsx
import MyInput from './MyInput';
const onChangeHandler = (val) => {
console.log(val);
};
export default {
render() {
return (
<MyInput onChange={onChangeHandler} />
);
},
};
and MyInput.jsx
export default {
props: {
onChange: {
type: Function,
},
},
render() {
// as Sphinx suggested it should be this.$props.onChange
return (
<input onChange={this.$props.onChange} />
);
},
};
But this.onChange is undefined:
How to properly use this.onChange prop in MyInput component?
CodePen
Here you can find CodePen with implementation of my problem:
https://codepan.net/gist/13621e2b36ca077f9be7dd899e66c056
Don't start your prop name with on. The 'on' prefix in reserved.
Credits to:
nickmessing - see his answer
Check Vue API: instance property=$props, you should use
_this.$props like below demo:
Vue.config.productionTip = false
Vue.component('child', {
props: {
onChange: {
type: Function,
default: function () {console.log('default')}
},
},
render: function (h) {
let self = this
return h('input', {
on: {
change: function (e) {
var test;
(test = self.$props).onChange(e)
}
}
})
}
})
Vue.component('container1', {
render: function (h) {
return h('child', {
props: {
onChange: this.printSome
}
})
},
methods: {
printSome: function () {
console.log('container 1 custom')
}
}
})
Vue.component('container2', {
render: function (h) {
return h('child', {
props: {
onChange: this.printSome
}
})
},
methods: {
printSome: function () {
console.log('container 2 custom')
}
}
})
new Vue({
el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<h3>Container 1</h3>
<container1></container1>
<h3>Container 2</h3>
<container2></container2>
</div>

How to save axios request in vue.js store correclty?

I would like to create dynamic sidebar links pulled from an API.
This is the theme I'm working with:
https://admin.vuebulma.com/#/
Note the charts link that has a list of children in the sidebar..
I want to make an API request - lets say for charts and create each child (see charts.js) for each element return in the API request.
In the example below, the data objects are hardcoded - I would like to remove this and dynamically create each child using a for loop for each element in the api request.body
/store/modules/menu/index.js
import * as types from '../../mutation-types'
import lazyLoading from './lazyLoading'
import charts from './charts'
import components from './components'
import dashboard from './dashboard'
// show: meta.label -> name
// name: component name
// meta.label: display label
const state = {
items: [
dashboard,
charts,
components
]
}
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
}
/store/modules/menu/charts.js
import lazyLoading from './lazyLoading'
export default {
name: 'Charts',
path: '/charts',
meta: {
icon: 'fa-bar-chart-o',
expanded: false,
link: 'charts/index.vue'
},
component: lazyLoading('charts', true),
children: [
{
name: 'Chartist',
path: 'chartist',
component: lazyLoading('charts/Chartist'),
meta: {
link: 'charts/Chartist.vue'
}
},
{
name: 'Chartjs',
path: 'chartjs',
component: lazyLoading('charts/Chartjs'),
meta: {
link: 'charts/Chartjs.vue'
}
},
{
name: 'Peity',
path: 'peity',
component: lazyLoading('charts/Peity'),
meta: {
link: 'charts/Peity.vue'
}
},
{
name: 'Plotly',
path: 'plotly',
component: lazyLoading('charts/Plotly'),
meta: {
link: 'charts/Plotly.vue'
}
}
]
}
sidebar.vue
<template>
<aside class="menu app-sidebar animated" :class="{ slideInLeft: show, slideOutLeft: !show }">
<p class="menu-label">
General
</p>
<ul class="menu-list">
<li v-for="(item, index) in menu">
<router-link :to="item.path" :exact="true" :aria-expanded="isExpanded(item) ? 'true' : 'false'" v-if="item.path" #click.native="toggle(index, item)">
<span class="icon is-small"><i :class="['fa', item.meta.icon]"></i></span>
{{ item.meta.label || item.name }}
<span class="icon is-small is-angle" v-if="item.children && item.children.length">
<i class="fa fa-angle-down"></i>
</span>
</router-link>
<a :aria-expanded="isExpanded(item)" v-else #click="toggle(index, item)">
<span class="icon is-small"><i :class="['fa', item.meta.icon]"></i></span>
{{ item.meta.label || item.name }}
<span class="icon is-small is-angle" v-if="item.children && item.children.length">
<i class="fa fa-angle-down"></i>
</span>
</a>
<expanding v-if="item.children && item.children.length">
<ul v-show="isExpanded(item)">
<li v-for="subItem in item.children" v-if="subItem.path">
<router-link :to="generatePath(item, subItem)">
{{ subItem.meta && subItem.meta.label || subItem.name }}
</router-link>
</li>
</ul>
</expanding>
</li>
</ul>
</aside>
</template>
<script>
import Expanding from 'vue-bulma-expanding'
import { mapGetters, mapActions } from 'vuex'
export default {
components: {
Expanding
},
props: {
show: Boolean
},
data () {
return {
isReady: false
}
},
mounted () {
let route = this.$route
if (route.name) {
this.isReady = true
this.shouldExpandMatchItem(route)
}
},
computed: mapGetters({
menu: 'menuitems'
}),
methods: {
...mapActions([
'expandMenu'
]),
isExpanded (item) {
return item.meta.expanded
},
toggle (index, item) {
this.expandMenu({
index: index,
expanded: !item.meta.expanded
})
},
shouldExpandMatchItem (route) {
let matched = route.matched
let lastMatched = matched[matched.length - 1]
let parent = lastMatched.parent || lastMatched
const isParent = parent === lastMatched
if (isParent) {
const p = this.findParentFromMenu(route)
if (p) {
parent = p
}
}
if ('expanded' in parent.meta && !isParent) {
this.expandMenu({
item: parent,
expanded: true
})
}
},
generatePath (item, subItem) {
return `${item.component ? item.path + '/' : ''}${subItem.path}`
},
findParentFromMenu (route) {
const menu = this.menu
for (let i = 0, l = menu.length; i < l; i++) {
const item = menu[i]
const k = item.children && item.children.length
if (k) {
for (let j = 0; j < k; j++) {
if (item.children[j].name === route.name) {
return item
}
}
}
}
}
},
watch: {
$route (route) {
this.isReady = true
this.shouldExpandMatchItem(route)
}
}
}
</script>
Not sure why I can't figure out how to do this.
Update:
Here's an example of the API I'm calling:
[
{
"id": 1,
"name": "test1",
"os": "windows",
"url": "https://test.com"
},
{
"id": 2,
"name": "test2",
"os": "ios",
"url": "https://test.com"
},
{
"id": 1,
"name": "test3",
"os": "windows",
"url": "https://test.com"
},
]
Create a copy of routes of charts in store
const state = {
chartsRoutes: []
}
Create a computed property in component
computed: {
chartsRoutes () {
return this.$store.state.chartsRoutes
}
}
Use v-for to render chartsRoutes into router-links in component
Create a mutation to modify store and router
// import router
const mutations = {
'update-charts-routes': function (state, payload) {
const { chartsRoutes } = payload
state.chartsRoutes = chartsRoutes.map(r => {
return {
path: '/your/custom/path/according/to/response'
// other params
}
})
router.addRoutes(state.chartsRoutes)
}
}
Create an action
const actions = {
'reload-charts': function ({commit, dispatch}, data) {
return new Promise((resolve, reject) => {
const r = {
method: 'get',
url: data.url,
// add more options, e.g. header or auth
}
axios.request(r)
.then(resp => {
commit('update-charts-routes', { chartsRoutes: resp.data })
resolve()
})
.catch(err => {
// handle error
reject(err)
})
}
}
}
}
Dispatch action
this.$store.dispatch('reload-charts', { url: 'http://some.host/path/to/url' })
.then(() => {
// other stuff
})

Categories