link2603 link2604 link2605 link2606 link2607 link2608 link2609 link2610 link2611 link2612 link2613 link2614 link2615 link2616 link2617 link2618 link2619 link2620 link2621 link2622 link2623 link2624 link2625 link2626 link2627 link2628 link2629 link2630 link2631 link2632 link2633 link2634 link2635 link2636 link2637 link2638 link2639 link2640 link2641 link2642 link2643 link2644 link2645 link2646 link2647 link2648 link2649 link2650 link2651 link2652 link2653 link2654 link2655 link2656 link2657 link2658 link2659 link2660 link2661 link2662 link2663 link2664 link2665 link2666 link2667 link2668 link2669 link2670 link2671 link2672 link2673 link2674 link2675 link2676 link2677 link2678 link2679 link2680 link2681 link2682 link2683 link2684 link2685 link2686 link2687 link2688 link2689 link2690 link2691 link2692 link2693 link2694 link2695 link2696 link2697 link2698 link2699 link2700 link2701 link2702 link2703 link2704 link2705 link2706 link2707 link2708 link2709 link2710 link2711 link2712 link2713 link2714 link2715 link2716 link2717 link2718 link2719 link2720 link2721 link2722 link2723 link2724 link2725 link2726 link2727 link2728 link2729 link2730 link2731 link2732 link2733 link2734 link2735 link2736 link2737 link2738 link2739

[Vue.js] Vuex state not updating from local storage

I’m having some problems getting the Vuex state to update from local storage when visiting a route directly from the address bar in an incognito window.

When visiting the route /shop/:key? directly (for example clicking a link from an email) the mounted action getDetail is executed and local storage is updated but the state is not reevaluated - it still says undefined as if the key doesn’t exist.

Is there a way to get the state to be reevaluate or does it have to be updated in the setDetail mutation?

const state = {
key: localStorage.getItem(‘key’),
details: null
}

const actions = {
async getDetail({ commit }, key) {
const details = await Service.detail(key)
commit(‘setDetail’, details)
}
}

const mutations = {
setDetail(state, details) {
state.details = details
}
}

const Service = {
detail: async function(key) {
const url = `/store/${key}/`
const response = await ApiService.get(url)
localStorage.setItem(‘key’, key)
return response.data
}
}

Solution :

The state will be re-evaluated if a mutation (or action) occurs. You could change the state directly but this is advised against as it breaks reactivity.

LocalStorage is not reactive and as such you need to update the state using a mutation or an action.

[Vue.js] Long action is locking renderer process

there is a function that I’m calling in a component template. It reacts on changes in a vue-select component.

<v-select
v-model=”model”
:options=”eventList”
:placeholder=”currentEventTitle”
v-on:input=”selectedEvent”
taggable
label=”name”
\></v-select>

</template>
<script>

methods: {
…mapActions({
selectedEvent: “selectedEvent”
})
}

This selectedEvent action that is being dispatched contains many HTTP calls, commits to the store, and calls to other actions. The issue is that once this has been called, the UI completely locks and doesn’t update until about halfway through the function.

How should I call an action that will commit (mutate), and dispatch other actions, and not lock the UI?

Solution :

If vuex observer/ reactivity is the reason for the ui block one can simply freeze big objects to prevent reactivity:

actions = {

// …,
assignBigData(context, data){
//…
// mutate data
const clone = {…data}
context.commit(‘commiter’, Object.freeze(clone))
},
selectedEvent: async function(context, …args){

const response = await request()

context.dispatch(‘assignBigData’, Object.freeze(response))
}
}

[Vue.js] Router-view not load second component

The redirect to new link and styles css works, but no method or event inicialize on the new component.
The component navbar keep going perfectly and the inspector not display no error. If reload page, all works again. Apologize my english

main.js

import vue.js from ‘vue’
import VueRouter from ‘vue-router’
import Routes from ‘./routes’
import App from ‘./componentes/App.vue’
Vue.use(VueRouter)
const router = new VueRouter({
routes: Routes,
// mode: ‘history’`Texto preformateado`
});

const app = new Vue({
router,
el: ‘#app’,
render: h => h(App)
})

app.vue

<template>
<div id=”app”>
<encabezado></encabezado>
<!– <keep-alive> –>
<router-view></router-view>
<!– </keep-alive> –>
</div>
</template>
<script>
import encabezado from ‘./encabezado.vue’
export default {
name: ‘app’,
components: {
encabezado
},
}
</script>

route.js

import Financiero from ‘./componentes/vistas/financiero.vue’;
import Principal from ‘./componentes/principal/principal.vue’;
export default [
{
path: ‘/financiero’,
component: Financiero,
name: ‘Financiero’
},
{ path: ‘/principal’, component: Principal, name: ‘principal’ },
]

component

<template>
<div :class=”{dvContainer:movil}”>
<div v-if=”movil” class=”fixed-action-btn”>
<a class=”btn-floating btn-large sidenav-trigger pulse waves-effect waves-light purple darken-3” href=”#” data-target=”slide-out”>
<i class=”large material-icons”>chat</i>
</a>
</div>
<ul id=”slide-out” class=”collapsible” :class=”{sidenav: movil, enCelularSide: movil, menuLateral: !movil }” style=”padding-bottom: 30px !important;”>
<li class=”active”>
<div class=”collapsible-header” ><i class=”material-icons”>filter_drama</i>Analisar proyecto</div>
<div class=”collapsible-body cuerpoItemColapse”>
<mapa></mapa>
</div>
</li>
</ul>
</div>
</template>
<script>
import evaluacion from ‘../navegacion/evaluacion.vue’
import mapa from ‘../navegacion/mapa.vue’
import resumen from ‘../navegacion/resumen.vue’
export default{

Solution :

import Financiero from ‘@/componentes/vistas/financiero.vue’;
import Principal from ‘ @/componentes/principal/principal.vue’;

Example here

[Vue.js] style backgroundColor for vue list

there is a problem to change background color for active list.
if someone knows and help me i would be very appreciate.

Since when taking a list contents from data function to use v-for in HTML tag, i got confused how to make active list and style it.

Solution :

<template>
<div class=”nav” :class=”{ openMenu: menuVisible }”>
<div class=”mouseover nav-toggle” @click=”sideBarToggle”>
<a @mouseover=”mouseover” @mouseleave=”mouseleave”>
<img :src=”image_src” class=”nav-image”>
</a>
</div>

<div id=”nav-content”>
<ul class=”nav-list”>
<li
v-for=”(item, index) in items”
:key=”item.list”

class=”nav-list-contents”
\>
<a>
<router-link tag=”div” :to=”item.url” :class=”{ ‘active’: index === 0 }”>
<i :class=”item.icon”/>
{ item.list }
</router-link>
</a>
</li>
</ul>
</div>
</div>
</template>

<script>
import { createNamespacedHelpers } from “vuex”;
const { mapState, mapMutations } = createNamespacedHelpers(“moduleMenubar”);
import Cookies from “js-cookie”;
const hamburgerMenu = Cookies.get(“openAndClose”);

export default {
data: function() {
return {
image_src: “humburger1.png”,
items: [
{ icon: “fa fa-fw fa-building”, list: “”, url: “/company” },
{ icon: “fa fa-fw fa-user”, list: “”, url: “/user” },
{ icon: “fa fa-fw fa-database”, list: “”, url: “/master” },
{
icon: “fa fa-fw fa-wrench”,
list: “”,
url: “/legal_maintenance”
},
{ icon: “fa fa-fw fa-newspaper”, list: “”, url: “/info” },
{
icon: “fa fa-fw fa-sitemap”,
list: “”,
url: “/grouping”
}
]
};
},
computed: {
…mapState([“menuVisible”])
},
methods: {
…mapMutations([“sideBarToggle”]),
mouseover: function() {
if (!this.menuVisible) {
this.image_src = “migi.png”;
} else {
this.image_src = “hidari.png”;
}
},
mouseleave: function() {
this.image_src = “humburger1.png”;
}
}
};
</script>

<style lang=”scss” scoped>
@import “./public/common”;

.active {
background-color: red;
width: 200px;
height: 50px;
}

.fa {
margin-right: 10px;
}

.nav-image {
width: 15px;
height: 15px;
}

.nav {
font-size: 15px;
width: 40px;
margin: left;
color: white;
height: 100%;
padding-top: 10px;
background-color: #ab2a3e;
position: relative;
transition: width 0.3s ease 0;
cursor: pointer;
/**/
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.nav::-webkit-scrollbar {
display: none;
}

.nav-list {
padding: 10px; /* */
margin-top: 50px;

.nav-list-contents {
cursor: pointer;

list-style: none;
width: 200px;
height: 50px;
}

.nav-list-contents:hover {
background-color: black;
}
}

.nav.openMenu {
width: 200px;
}

.nav-toggle {
background-color: white;
padding: 6px 6px;
margin: 10px 5px;
margin-bottom: 10px;
float: right;
}
</style>

Solution 2:

<div v-for=”item in list” :style=”{backgroundColor: item.color, backgroundImage: ‘url(‘ + item.imageUrl + ‘)’}”></div>

data () {
return {
list: [
{color: ‘#ff0’, imageUrl: ‘https://…..’}
]
}
}

[Vue.js] vuex - unknown action type (can't dispatch my action)

I was trying moving my store into separate modules and couldn’t figure out how to dispatch my actions. I can access the state values, but not the action.

Bear in mind this project I’m doing exclusively to learn vue, so I’m definitely a beginner here.

src/store/store.js (acts as my index inside the store folder)

import vue.js from ‘vue’
import Vuex from ‘vuex’
import axios from ‘axios’

import posts from ‘./modules/posts’
import comments from ‘./modules/comments’

Vue.use(Vuex)
axios.defaults.baseURL = ‘https://someapi.com/'

export default new Vuex.Store({
name: ‘store’,
namespaced: true,
modules: {
posts,
comments
},
})

src/store/modules/posts.js

import vue.js from ‘vue’
import Vuex from ‘vuex’
import axios from ‘axios’

Vue.use(Vuex)
axios.defaults.baseURL = ‘https://someapi.com/'

export default new Vuex.Store({
name: ‘posts’,
state: {
posts: [],
post: {}
},
mutations: {
retrievePosts(state, posts){
state.posts = posts
},
retrievePost(state, post){
state.post = post
},
},
actions: {
retrievePosts (context) {
axios.get(‘/blog/post/all’)
.then(response => {
var posts = response.data.data
context.commit(‘retrievePosts’, posts)
})
.catch(error => {
console.log(error)
})
},
retrievePost (context, slug) {
axios.get(‘/blog/post/all’)
.then(response => {
var post = response.data.data[1]
context.commit(‘retrievePost’, post)
})
.catch(error => {
console.log(error)
})
},
}

})

src/components/BlogPostList.vue

<template>
<div class=”postList”>
<b-container v-for=”post in this.$store.state.posts.posts” :key=”post.id” class=”post”>
<b-row>
<b-col>
<h1><a :href=”‘post/‘ + post.slug”>{ post.title }</a></h1>
</b-col>
</b-row>
<b-row>
<b-col cols=”10”>
<h4>{ post.subtitle }</h4>
</b-col>
<b-col>
<small>by { post.author_id } </small>
</b-col>
</b-row>
<b-row>
<b-col>
<!– <p v-html=”post.body.substring(0, 1000) + ‘…’”></p> –>
<span class=”post-text” v-html=”post.body”/>
</b-col>
</b-row>
<br />
<b-row>
<b-col>
<dx-button style=”float:right”
text=”read more”
@click=”buttonClicked”
/>
</b-col>
</b-row>
<hr>
</b-container>
</div>
</template>

<script>
import DxButton from “devextreme-vue/button”;

export default {
components: {
DxButton
},
name: ‘BlogPostList’,
created () {
this.$store.dispatch(‘retrievePosts’)
},
methods: {
buttonClicked: function() {
alert(“The Button was clicked”);
}
}
}
</script>

<!– Add “scoped” attribute to limit CSS to this component only –>
<style scoped lang=”scss”>
.postList {
h1 {
font-size: 50px;
}
.post-text {
text-align: justify;
}
}
</style>

I’ve tried the most “recommended” fix I found, which is changing

this.$store.dispatch(‘retrievePosts’)

to

this.$store.dispatch(‘posts/retrievePosts’)

but I get the same error on console…

Thanks in advance!

Solution :

This might be because you created another Vuex store inside the posts module which you’ve added as module in the main store.js.

Try modifying the posts module to the following.

import axios from ‘axios’;

axios.defaults.baseURL = ‘https://someapi.com/';

export default {
name: ‘posts’,
state: {
posts: [],
post: {}
},
mutations: {
retrievePosts(state, posts) {
state.posts = posts;
},
retrievePost(state, post) {
state.post = post;
}
},
actions: {
retrievePosts(context) {
axios
.get(‘/blog/post/all’)
.then((response) => {
var posts = response.data.data;
context.commit(‘retrievePosts’, posts);
})
.catch((error) => {
console.log(error);
});
},
retrievePost(context, slug) {
axios
.get(‘/blog/post/all’)
.then((response) => {
var post = response.data.data[1];
context.commit(‘retrievePost’, post);
})
.catch((error) => {
console.log(error);
});
}
}
};

[Vue.js] (Nuxt.js/Vue.js) - Script only works once, stops working after changing route/refreshing the page

I’m “transferring” my projects website to Vue.js with Nuxt.js on top.
I’m copying over the contents of all the files stored in the remote server to the local “static” folder.

Everything is working fine, except the JavaScript that runs for the first time the page is loaded, but if I switch to another page through the routes, or refresh the current page, the JavaScript will stop working.

For instance, there’s this page in my projects website: http://archy-user.name/drag/

Where you can basically drag the image to other boxes, with class changes when you hover over any box with the image.

I copied over the CSS to the local static folder, works fine, copied over the JavaScript, it only works once and stops working after changing route/refreshing the page…

It works just like the website when you first load the page, but after reloading/changing route the script just stops working, and there are no class changes when hovering over boxes, etc… Albeit working fine the first time the page is loaded.

Yesterday when researching about this, answers to related questions were saying that this happened because the script is ran only once when the page is loaded, so when there are route changes or the page is refreshed, the script doesn’t run again.

Some suggested adding the function supposed to be executed when the page is loaded to the “created()” method inside “export default” in the vue.js component.

However, in my case, I don’t have something that I actually want to execute every time the page is loaded, rather, to execute only a certain portion of the script, which would be specific functions triggered only when certain actions are performed by the user in the page through interactions…

Loading the script every time wouldn’t be necessary, as the interactions may not even happen, making the script useless, as well as the load time. Not to mention the mess that the component would become if I were to add the whole script to the “created” method.

Basically I didn’t find any real fix for this issue, only workarounds that cause side-effects…

Here’s how my components are structured (below component is from http://archy-user.name/drag):

<template>
<div class=”container”>
<div class=”box”>
<div class=”fill” draggable=”true”></div>
</div>
<div class=”box”></div>
<div class=”box”></div>
<div class=”box”></div>
<div class=”box”></div>
<div class=”box”></div>
</div>
</template>

<script>
export default {
name: ‘Drag’,
head: {
link: [ { rel: ‘stylesheet’, type: ‘text/css’, href: ‘css/drag.css’} ],
script: [ { src: ‘js/drag.js’ } ]
}
}
</script>

<style>

</style>

Any tips to fix this issue? Or workarounds to my situation?

PS - Everytime I close the tab and open a new one, the scripts works again until the page is refreshed/route is changed

Solution :

You can rewrite the code in vue.js component style. it will be more clear. and easy to reuse

<template>
<div class=”drag”>
<div
v-for=”n in range”
:key=”n”
class=”box”
@dragenter=”dragEnter”
@dragover=”dragOver”
@dragleave=”dragLeave”
@drop=”dragDrop”
\>
<div
v-if=”n === 1”
class=”fill”
draggable=”true”
@dragstart=”dragStart”
@dragend=”dragEnd”
/>
</div>
</div>
</template>

<script>
export default {
name: ‘Drag’,
props: {
range: {
type: Number,
default: 5
}
},
data() {
return {
dragged: ‘’
}
},
methods: {
dragEnter: function(e) {
e.target.className += ‘ hovered’
},

dragOver: function(e) {
e.preventDefault()
},

dragLeave: function(e) {
e.target.className = ‘box’
},

dragDrop: function(e) {
e.target.className = ‘box’
e.target.appendChild(this.dragged)
},

dragStart: function(e) {
e.target.className += ‘ ondrag’
this.dragged = e.target
setTimeout(() => (e.target.className = ‘invisible’), 0)
},

dragEnd: function(e) {
e.target.className = ‘fill’
}
}
}
</script>

<style>
.drag {
background-color: darksalmon;
display: flex;
justify-content: flex-start;
}

.box {
background-color: white;
width: 160px;
height: 160px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
margin-right: 15px;
border: 3px white solid;
}

.fill {
background-image: url(‘http://source.unsplash.com/random/150x150');
width: 150px;
height: 150px;
margin: 5px 5px;
cursor: pointer;
}

.ondrag {
border: solid #ccc 4px;
}

.invisible {
display: none;
}

.hovered {
background: #f4f4f4;
border-style: dashed;
}
</style>

Solution 2:

this is ugly and totally a bad idea but is exact what you ask. using nuxt you can bypass the navigation issue using the old link element instead router-link or nuxt-link, with that you force to refresh the entire page.

remember nuxt in universal mode, server render the first page, the rest of the navigation is live reload as SPA, the issue is realted with addEventListener, because in the first visit you add this but you never remove them.

So using a link element for navigation you force to refresh the entire page, every time will be the first visit. after that to be sure the elements exist before apply you can add the script at the bottom of the template, the page will be:

<template>
<div>
<a href=”/“>
go Home
</a>
<div class=”container”>
<div class=”box”>
<div
class=”fill”
draggable=”true”
dragstart=”dragStart”
dragend=”dragEnd”
/>
</div>
<div class=”box” />
<div class=”box” />
<div class=”box” />
<div class=”box” />
<div class=”box” />
</div>
<script src=”js/drag.js” />
</div>
</template>

<script>
export default {
name: ‘Drag’,
head: {
link: [{ rel: ‘stylesheet’, type: ‘text/css’, href: ‘css/drag.css’ }]
}
}
</script>

I add a link to / for testing

[Vue.js] Cypress vue.js CypressError Timed out retrying Expected to find content

Problem description

CypressError: Timed out retrying: Expected to find content: ‘mm’
within the element: but
never did.

there is mounted the component on cypress with mock data. Then I tried to get an element using below source code and receive the above error:

cy.get(‘#edit-highlights-form1’).contains(‘2m’);

Current situation

there is a vue.js application which contains a component and it consumes data from an API. Please find the source code below that I tried to write unit test for that component using Cypress.

Code:

describe(‘Edit highlights component ‘, () => {
beforeEach(mountVue({
template,
components,
data
}))

it(‘Stub the mock data to the component edit highlights’, () => {

cy.server();
cy.route({
method: ‘GET’,
url: ‘/drupal/api/v1/getHighlightData/*‘,
response: data().highlightModel

}).as(‘apiHighligihtData’);
mountVue({
template,
components,
data
});
cy.get(‘#edit-highlights-form1’).contains(‘2m’);
});
});

I got the tutorial from https://github.com/bahmutov/cypress-vue-unit-test/blob/master/cypress/integration/ajax-list-spec.js

Solution :

I was not able to get the element #edit-highlights-form1 using the cypress command cy.get(). I solved this issue by using the command cy.get(“#edit-highlights-form1”).should( “have.value”, ‘2m’ );

[Vue.js] How to submit vue-multiselect selected option - Vue.js

there is this form with two inputs, Options (multiselect) and Amount

there is managed to add Amount to new Form but I failed to add the selected option.

HTML

<template>
<div class=”container”>
<form @submit.prevent=”submitForm” @keydown=”form.onKeydown($event)”>
<h5 class=”form-header”>
vue.js JS form - Laravel
</h5>
<div class=”form-group row”>
<label class=”col-form-label col-sm-4” for=””> Options</label>
<div class=”col-sm-8”>
<multiselect
v-model=”selected”
:options=”options”>
</multiselect>
</div>
</div>
<div class=”form-group row”>
<label class=”col-form-label col-sm-4” for=””> amount</label>
<div class=”col-sm-8”>
<input v-model=”form.amount” type=”text” name=”amount” class=”form-control” >
</div>
</div>
<div class=”form-buttons-w text-right”>
<button class=”btn btn-primary” type=”submit”>Save</button>
</div>
</form>
</div>
</template>

JS

<script>
export default {
data(){
return {
selected: null,
options: [‘One’, ‘Two’, ‘Three’],
form: new Form({
amount : ‘’
})
}
},
methods : {
submitForm(){
this.form.post(‘api/transaction’)
}
},
mounted() {

}
}
</script>

How can I get the selected option (multiselect) in submitted data ? The same way am getting entered amount.

Solution :

You can do like this

<div class=”form-group row”>
<label class=”col-form-label col-sm-4” for=””> Options</label>
<div class=”col-sm-8”>
<multiselect
v-model=”form.selected”
:options=”options”>
</multiselect>
</div>
</div>
<script>
export default {
data(){
return {
selected: null,
options: [‘One’, ‘Two’, ‘Three’],
form: new Form({
amount : ‘’,
selected:[],
})
}
},
methods : {
submitForm(){
this.form.post(‘api/transaction’)
}
},
mounted() {

}
} </script>

[Vue.js] Conditional attribute in nuxt.config.js

when developing a website using nuxt.js, and would like to have a configuration option in nuxt.config.js conditionally: I would like to change the router base when I run npm run generate (building static)

Here is the full config file for development environment (npm run dev):

const pkg = require(‘./package’)

module.exports = {
mode: ‘universal’,

// if I uncomment the following three lines, the config is OK for npm run generate.
// router: {
// base: ‘/app/‘
// },

/*
** Headers of the page
*/
head: {
title: pkg.name,
meta: [
{ charset: ‘utf-8’ },
{ name: ‘viewport’, content: ‘width=device-width, initial-scale=1’ },
{ hid: ‘description’, name: ‘description’, content: pkg.description }
],
link: [
{ rel: ‘icon’, type: ‘image/x-icon’, href: ‘/favicon.ico’ },
{ rel: ‘stylesheet’, href: ‘https://fonts.googleapis.com/css?family=Montserrat:400,500,600&subset=latin-ext' }
]
},

/*
** Customize the progress-bar color
*/
loading: { color: ‘#fff’ },

/*
** Global CSS
*/
css: [
‘@/assets/css/main.scss’,
],

/*
** Plugins to load before mounting the App
*/
plugins: [
],

/*
** Nuxt.js modules
*/
modules: [
// Doc: https://axios.nuxtjs.org/usage
‘@nuxtjs/axios’,
// Doc: https://bootstrap-vue.js.org/docs/
‘bootstrap-vue/nuxt’,
// Doc: https://github.com/vanhoofmaarten/nuxt-mq
[
‘nuxt-mq’,
{
// Default breakpoint for SSR
// Breakpoints are bootstrap-vue.js Breakpoints
// Doc: https://bootstrap-vue.js.org/docs/components/layout
defaultBreakpoint: ‘default’,
breakpoints: {
xs: 576, // 576 not included
sm: 768, // 768 not included
md: 992, // 992 not included
lg: 1200, // 1200 not included
xl: Infinity
}
}
]
],
bootstrapVue: {
bootstrapCSS: false, // or `css`
bootstrapVueCSS: false // or `bvCSS`
},
/*
** Axios module configuration
*/
axios: {
// See https://github.com/nuxt-community/axios-module#options
},

serverMiddleware: [
‘~/api/contact’
],

/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend(config, ctx) {
// Run ESLint on save
if (ctx.isDev && ctx.isClient) {
config.module.rules.push({
enforce: ‘pre’,
test: /\.(js|vue)$/,
loader: ‘eslint-loader’,
exclude: /(node_modules)/
})
}
}
}
}

The config works fine for both settings (so it compiles, the app is running correctly), but I would like to make it automatic, as I often forget to uncomment the router settings when to see the static version.

I haven’t looked in the problem much, just read some SO questions and Googled a bit (for things like this: nuxt.js -> Howto configure production/development settings or this: https://github.com/nuxt/nuxt.js/issues/2940).

Solution :

You could use an environment variable and include a condition on this environment variable in the config file:

const pkg = require(‘./package’)

let config = {
mode: ‘universal’,
head: {},

}

if (process.env.NODE_GENERATION_TYPE === ‘static’) {
config.router = {
base: ‘/app/‘
}
}

module.exports = config

You would then need to use the following command line to generate the static website:

NODE_GENERATION_TYPE=static npm run generate

And you could also set up a script into package.json to make it prettier:

{
“scripts”: {
“generate:static”: “NODE_GENERATION_TYPE=static dev”,
“dev”: “…”
},

}

You would then be able to run it using npm run generate:static

[Vue.js] Can't access dynamic nested JSON property

Im creating a component to display a table based on JSON data, but nothing is shown for my column when the column’s property key is lieu.ville. How do I fix this?

Component:

<template>
<ul class=”list-group”>
<li class=”list-group-item list-group-item-info”>
<div class=”row”>
<div v-for=”col in colss” class=”” :class=”bootstrapClass”>{col | capitalize}</div>
</div>
</li>

<li v-for=”(item,index ) in datas” class=”list-group-item” :key=”item[‘id’]“>
<div class=”row”>
<div v-for=”(value,i) in columns” class=”” :class=”bootstrapClass”>
<i>{ item[value] }</i>
</div>
</div>
</li>
</ul>

</template>

<script>
var csrf_token = $(‘meta[name=”csrf-token”]‘).attr(‘content’);

export default {
props: [‘colss’,’columns’,’datas’,’urlajax’],

computed: {
bootstrapClass: function() {
return ‘col-sm-‘ + (12 / this.colss.length );
},
},

mounted: function () {
console.log(“testtttt”);
},

filters: {
capitalize: function (str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
},
}
</script>

vue.js bootstrap:

var listSessions = new Vue({
el: ‘#listmodalSessions’,
data: {
// collection des proprits
columns: [‘id’, ‘ref_session’, ‘datesPeriodesConcat’,’lieu.ville’],
// collection d’affichage
colss: [‘Id’, ‘Ref Session’, ‘Dates’,’Ville’],
datas: [],
urlPrefix:’/admin/User/‘ ,
},
methods: {
showModal () {
$(‘#sessionsModal’).modal(‘show’);
},

hideModal () {
$(‘#sessionsModal’).modal(‘hide’);
},
// id Item reprsente l’id du user auquel appartiennent les permissions de la liste
getDatas(idItem){

var MonThis = this;
MonThis.datas = [];

$.ajax({
url: this.urlajax,
cache: false,
dataType: ‘json’,
success: function (data, textStatus, jqXHR) {
if (jqXHR.status === 200) {
MonThis.datas = data;
var index = 0;
}
}
});
},

},
components: {modalTable},
});

Sample data:

[{
“id”: 6735,
“formation_id”: 8376,
“statut_session_id”: 1,
“lieu_id”: 1,
“ref_session”: “1 333 7020 19S 10 41 01”,
“prixSession”: 1385,
“intraInter”: 1,
“intraInSitu”: 0,
“commentaires”: null,
“created_at”: “2019-03-25 17:31:29”,
“updated_at”: “2019-03-25 17:31:29”,
“deleted_at”: null,
“periodes”: [{
“id”: 5634,
“session_id”: 6735,
“salle_id”: 43,
“date_debut”: “2019-10-07”,
“date_fin”: “2019-10-11”,
“commentaires”: null,
“created_at”: “2019-03-25 17:31:29”,
“updated_at”: “2019-03-25 17:31:29”,
“deleted_at”: null
}],
“lieu”: {
“id”: 1,
“ville”: “Paris”,
“cp”: “75”,
“region”: null,
“displaySite”: 1,
“created_at”: “2019-03-13 13:24:36”,
“updated_at”: “2019-03-13 13:24:36”
},
“datesPeriodesConcat”: “2019-10-07 au 2019-10-11”,
“lieu2Display”: “Paris”,
“link”: “<a class=\“btn btn-info\“ href=\“\/session\/6735\“>Session<\/a>”
}]

Solution :

The problem…

item[‘lieu.ville’] is not the same as item.lieu.ville. The former looks up a property named lieu.ville in item, which doesn’t exist in this case. The latter looks up lieu in item, then ville in the result.

One way to solve this is to programmatically split the given object key by . so that the correct property can be resolved:

methods: {
getValue(item, key) {
return key.split(‘.’).reduce((obj,k) => obj[k] || {}, item)

/** The one-liner above is equivalent to the following **/

const keys = key.split(‘.’)
for (const k of keys) {
if (!item) break
item = item[k]
}
return item || {}
}
},

Suboptimal usage…

You could use that getValue method in the template like this:

<li v-for=”item in datas”>
<div class=”row”>
<div v-for=”key in columns”>
<i>{ getValue(item, key) }</i>
</div>
</div>
</li>

But using such methods in the template is inefficient because the method is called on every render, producing the same result each time.

Something better…

You could use a computed property instead, which gets cached to avoid unnecessary recalculations on render. The computed prop (e.g., named tableData) would contain new table data that has its column’s items already resolved:

computed: {
tableData() {
return this.datas.map(item => ({
…item,
columns: this.columns.map(key => this.getValue(item, key))
}))
}
}

Then, you could use the tableData prop in the template like this:

<li v-for=”item in tableData”>
<div class=”row”>
<div v-for=”value in item.columns”>
<i>{ value }</i>
</div>
</div>
</li>