VueJS - onclick to make active on new Array entry not working - vue.js

https://codepen.io/donnieberry97/pen/GGKQRN
var demo = new Vue({
el: '#main',
data: {
services: [
{
name: 'Item 1',
price: 200,
active: true
},
{
name: 'Item 2',
price: 500,
active: false
},
{
name: 'Item 3',
price: 700,
active: false
}
]
},
methods: {
addItem: function() {
var newItem= {
name:this.name,
price:this.price
};
this.services.push(newItem);
this.name="";
this.price="";
toggleActive();
},
toggleActive: function(f) {
f.active = !f.active;
},
total: function(){
var total=0;
this.services.forEach(function(f){
if(f.active){
total+=f.price;
}
});
return total;
}
}
});
When you use the input to add a new entry to the services array, upon clicking it afterwards, the active tag does not get applied to the new entry. It should turn blue and add to the total price but only the hover state works.

I've modified you code at method 'addItem' and use computed property total instead total method,have a look:
var demo = new Vue({
el: '#main',
data: {
services: [
{
name: 'Item 1',
price: 200,
active: true
},
{
name: 'Item 2',
price: 500,
active: false
},
{
name: 'Item 3',
price: 700,
active: false
}
]
},
computed: {
total () {
return this.services.reduce((last,item)=>last + parseInt(item.price) * item.active,0)
}
},
methods: {
addItem: function() {
var newItem= {
name:this.name,
price:this.price,
active: true
};
this.services.push(newItem);
this.name="";
this.price="";
},
toggleActive: function(f) {
f.active = !f.active;
}
}
});
* {
padding: 0;
margin: 0;
}
body{
font-family: 'Roboto', sans-serif !important;
}
h3 {
text-align:center;
padding: 2em 0em;
}
h5 {
padding: 1.5em 0.5em;;
box-sizing:border-box;
}
.container {
width:600px;
margin: 0 auto;
}
ul {
list-style:none;
}
li {
color:black;
border:1px solid #eeeeee;
padding:0.5em;
border-left: 5px solid #2196F3;
height:30px;
line-height:30px;
transition: 0.4s ease;
}
.active {
background-color:#2196F3;
color:white;
transition: 0.3s;
transition: 0.4s ease;
}
.active:hover {
background-color:#2196F3;
}
li:hover {
background-color:#82c4f8;
transition: 0.4s ease;
cursor:pointer;
}
span {
float:right;
}
#main {
box-shadow: 0 19px 38px rgba(0,0,0,0.0), 0 6px 12px rgba(0,0,0,0.22)
}
.text-center {
text-align:center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.27/vue.min.js"></script>
<div class="container">
<div id="main">
<div class="header"><h3>Click the services you wish to have:</div>
<ul>
<li class="group-item" v-for="service in services" v-on:click="toggleActive(service)" v-bind:class="{'active': service.active}">{{service.name}} <span>{{service.price | currency}}</span></li>
</ul>
<h5>Total is: {{total | currency}}</h5>
<input type="text" v-model="name" placeholder="name">
<input type="text" v-model="price" placeholder="price">
<button v-on:click="addItem()">Add Item</button>
</div>
</div>

The main issue that you're running into is you're calling an undefined function and not passing a parameter into your toggleActive function.
Since toggleActive is a Vue method, you'll need to use this to reference it correctly and use the function from your Vue instance; once that problem is fixed, you'll need to pass in the item that you're wanting to toggle, because the way that function is written it requires a parameter to update active status.
Here's how you could update your addItem function to get it working:
addItem: function() {
var newItem= {
name:this.name,
price:this.price,
active: false,
};
this.services.push(newItem);
this.name="";
this.price="";
this.toggleActive(this.services[this.services.length - 1]);
},
Also notice that I added the active property during item creation so that Vue treats this as a reactive property. Otherwise, your item will be stuck in the active state (after toggling it) and cannot become inactive on click. You could change this to just be active: true during creation (and remove the call to make it active completely) if all new items are supposed to be active on creation. I didn't do that, though, as I wanted to show how to fix the call to toggleActive.
You can view a forked and updated Codepen here if you'd like to see the code in a fully working state.

Related

Display after class based on amount of items in loop

I am displaying data in html using in loop, inside it I got a class
div(v-for="item in items" :key="item.id")
.circle
p {{ item.name }}
in my css I got
.circle::after
content: ''
background: grey
height: 5px
width: 130px
position: absolute
left: 0
top: 16px
z-index: -1
Can I display this after class only if there is more than 1 item in my loop?
As I said in the comments, ::after pseudo element is CSS construct so you cannot control it using Vue.
I see two possible solutions:
Define two classes - one with ::after and one without it and conditionally apply only one of the classes based on the number of items
You can actually do it entirely with CSS using :not(:only-child) pseude-selector. See example below
new Vue({
el: "#app",
data: {
toggle: true,
items: [
{ text: "Learn JavaScript", id: 1 },
{ text: "Learn Vue", id: 2 },
{ text: "Play around in JSFiddle", id: 3 },
{ text: "Build something awesome", id: 4 }
]
},
computed: {
computedItems() {
return this.toggle ? this.items : this.items.slice(0,1)
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
.circle:not(:only-child)::after {
content: " ➥";
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="toggle = !toggle">
Switch items
</button>
<div>
<div v-for="item in computedItems" :key="item.id" class="circle">
{{ item.text }}
</div>
</div>
</div>

Vue onclick display specific item

I have a question about Vue.
I want to add a class to a specific item:
<p v-on:click="display = !display">Rediger joke</p>
Display is False before and it change it to true.
And it works. But my problem is, that this onclick is inside an v-for loop, and i only want to put "display" on one "update-site" and not all of them. Can i do this or do I have to try a different setup?
Thanks a lot
I have this idea that might help you. The idea is you extend post object with for example visible property and when you click event triggered you change this property and add .display class. Please check this jsfiddle
template
<div id="app">
<article v-for="post in filteredPosts" :key="post.id">
{{post.name}}
<button #click="display(post)">show</button>
<div class="post-content" :class="{display: post.visible}">this is the part I want to display onclick</div>
<hr />
</article>
</div>
css
.post-content {
display: none;
}
.post-content.display {
display: block;
}
code
new Vue({
el: '#app',
data() {
return {
posts: []
};
},
created() {
//when you load posts. add visible property.
setTimeout(() => {
//posts from server
var postsFromServer = [{
id: 1,
name: 'Post One'
},
{
id: 2,
name: 'Post Two'
}
];
//add visible proprty.
this.posts = postsFromServer.map(post => {
return {
...post,
visible: false
};
});
}, 1000);
},
computed: {
filteredPosts() {
//do your filters
return this.posts;
}
},
methods: {
display(post) {
this.$set(post, 'visible', !post.visible);
}
}
});
I have an article, and i get the data from Firebase.
<article v-for="post in filteredPosts" :key="post.id">
{{post.name}}
<p v-on:click="display = !display"></p>
<div>this is the part I want to display onclick</div
</article>
updateInputs has display:none, but onclick I want it to be display as block:
.updateInputs.display {
display: block;
position: absolute;
top:0;
left:0;
bottom: 0;
background-color: white;
box-shadow: 4px 4px 10px black;
width: 100%;
height: auto;
overflow: hidden;
overflow-y: scroll;
padding-bottom: 10px;
}

How can I create a computed array in Vue and then render it using v-if all while using v-model on the computed array?

I've been trying to create a computed array which I then render using v-if. But it also needs to work with v-model. The reason it needs v-model is because it's part of a draggable list using vuedraggable.
Currently I get the following error Computed property "list" was assigned to but it has no setter.
The following code is my drag.vue component:
<template>
<div>
<draggable
v-model="list"
v-bind="dragOptions"
class="bigger-area"
#start="isDragging=true"
#end="isDragging=false"
>
<transition-group name="flip-list" type="transition">
<li
v-for="text in list"
:key="text"
id="list1"
class="drag-item flex flex-justify-betweeen"
>{{ text }}</li>
</transition-group>
</draggable>
</div>
</template>
<script>
import draggable from "vuedraggable";
export default {
name: "Drag",
data() {
return {
test: [],
lists: [
{
title: "0-6 months",
correctlyOrderedList: [
"Lifting Head",
"Rolling",
"Sitting (with support)"
]
},
{
title: "6-12 months",
correctlyOrderedList: [
"Crawling on stomach",
"Sitting (without support)",
"Stands with support and walks holding on",
"Rolls a ball"
]
},
{
title: "12-18 months",
correctlyOrderedList: ["Crawling", "Walks alone"]
},
{
title: "18 months – 2 years",
correctlyOrderedList: [
"Walks smoothly and turns corners",
"Walks upstairs with support",
"Begins running"
]
},
{
title: "2-3 years",
correctlyOrderedList: [
"Walks upstairs without support",
"Runs safely",
"Catches using body and arms"
]
},
{
title: "3-4 years",
correctlyOrderedList: ["Kicks a ball forwards", "Can hop on one foot"]
},
{
title: "4-5 years",
correctlyOrderedList: [
"Catches using only their hands",
"Can skip following a demonstration"
]
}
]
};
},
components: {
draggable
},
methods: {
fullArrayMethod() {
//Puts all statements into single array
let i;
let v;
let fullArrayInOrder = [];
for (i = 0; i < this.lists.length; i++) {
for (v = 0; v < this.lists[i].correctlyOrderedList.length; v++) {
fullArrayInOrder.push(this.lists[i].correctlyOrderedList[v]);
}
}
return fullArrayInOrder;
},
disorderedArrayMethod() {
//Randomizes array
let fullArrayInOrder = this.fullArrayMethod();
var copy = [],
n = fullArrayInOrder.length,
i;
// While there remain elements to shuffle…
while (n) {
// Pick a remaining element…
i = Math.floor(Math.random() * fullArrayInOrder.length);
// If not already shuffled, move it to the new array.
if (i in fullArrayInOrder) {
copy.push(fullArrayInOrder[i]);
delete fullArrayInOrder[i];
n--;
}
}
return copy;
},
chunk(array, size) {
const chunked_arr = [];
let index = 0;
while (index < array.length) {
chunked_arr.push(array.slice(index, size + index));
index += size;
}
return chunked_arr;
},
splitArrayFinalProduct() {
let disorderedArray = this.disorderedArrayMethod();
let finalArray = this.chunk(disorderedArray, 3);
return finalArray;
}
},
computed: {
dragOptions() {
return {
animation: 0,
group: "shared",
disabled: false,
ghostClass: "ghost"
};
},
list() {
return this.disorderedArrayMethod();
}
}
};
</script>
Context: I'm trying to create an application which consolidates multiple arrays into one. Randomises the array. The user can then put it back in order and then see if they got it right.
For anyone who may find it useful this is what worked for me. I can't explain the in's and outs of why it works so hopefully somebody smarter than me can elaborate.
To get the computed array variable to work with v-model and v-for I used map() as shown below:
let completeListOfStatements = this.lists.map(
d => d.correctlyOrderedList
);
My understanding of map() is it returns an array.
Then in the v-model I set it to the array that is within the object. This is the same one that I used map() on. This can be seen below.
<draggable
v-model="lists.correctlyOrderedList"
v-bind="dragOptions"
class="list-group"
#start="isDragging=true"
#end="isDragging=false"
>
For comparisons to the code in my question here's all the code from the component:
<template>
<div class="draggable-list-container">
<div
class="draggable-list-inner-container"
v-for="(statement, index) in splitCompleteListOfStatements"
:key="index"
>
<h1>{{ lists[index].title }}</h1>
<draggable
v-model="lists.correctlyOrderedList"
v-bind="dragOptions"
class="list-group"
#start="isDragging=true"
#end="isDragging=false"
>
<transition-group name="flip-list" type="transition">
<li
v-for="(statement, index) in statement"
:key="index + 'index'"
class="drag-item flex flex-justify-betweeen"
>{{ statement }}</li>
</transition-group>
</draggable>
</div>
<div class="submit-button-container">
<button class="btn" #click="revealAnswers">Reveal answers</button>
</div>
</div>
</template>
<script>
import draggable from "vuedraggable";
export default {
name: "Drag",
data() {
return {
lists: [
{
title: "0-6 months",
correctlyOrderedList: [
"Lifting Head",
"Rolling",
"Sitting (with support)"
]
},
{
title: "6-12 months",
correctlyOrderedList: [
"Crawling on stomach",
"Sitting (without support)",
"Stands with support and walks holding on",
"Rolls a ball"
]
},
{
title: "12-18 months",
correctlyOrderedList: ["Crawling", "Walks alone"]
},
{
title: "18 months – 2 years",
correctlyOrderedList: [
"Walks smoothly and turns corners",
"Walks upstairs with support",
"Begins running"
]
},
{
title: "2-3 years",
correctlyOrderedList: [
"Walks upstairs without support",
"Runs safely",
"Catches using body and arms"
]
},
{
title: "3-4 years",
correctlyOrderedList: ["Kicks a ball forwards", "Can hop on one foot"]
},
{
title: "4-5 years",
correctlyOrderedList: [
"Catches using only their hands",
"Can skip following a demonstration"
]
}
]
};
},
components: {
draggable
},
methods: {
disorderedArrayMethod(value) {
//Randomizes array
let fullArrayInOrder = value;
var copy = [],
n = fullArrayInOrder.length,
i;
// While there remain elements to shuffle…
while (n) {
// Pick a remaining element…
i = Math.floor(Math.random() * fullArrayInOrder.length);
// If not already shuffled, move it to the new array.
if (i in fullArrayInOrder) {
copy.push(fullArrayInOrder[i]);
delete fullArrayInOrder[i];
n--;
}
}
return copy;
},
revealAnswers() {this.splitCompleteListOfStatements[0].push("Hello")}
},
computed: {
dragOptions() {
return {
animation: 0,
group: "shared",
disabled: false,
ghostClass: "ghost"
};
},
splitCompleteListOfStatements() {
let completeListOfStatements = this.lists.map(
//Maps out full array (Basically loops through gathers the arrays and creates an array from them)
d => d.correctlyOrderedList
);
completeListOfStatements = completeListOfStatements.reduce(function(
//The map returns an array as the following [[a,b], [], []] etc. So this turns it into [a,b,c,d]
a,
b
) {
return a.concat(b);
}, []);
completeListOfStatements = this.disorderedArrayMethod(
completeListOfStatements
); //This sends it to a method that jumbles the array
var temp = [];
var preVal = 0;
var nextVal = 3;
for (var i = 0; i < 7; i++) {
temp.push(completeListOfStatements.slice(preVal, nextVal));
preVal = nextVal;
nextVal = nextVal + 3;
}
return temp;
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.title {
margin-bottom: 0.5em;
}
.submit-button-container {
margin-top: 5px;
}
.btn {
width: 10em;
height: 5em;
}
.draggable-list-container {
display: inline-block;
justify-content: center;
min-height: 200px;
}
.list-group {
min-height: 80px;
}
.drag-item {
justify-content: center;
padding: 15px 10px;
background-color: whitesmoke;
border: 1px solid black;
width: 20em;
margin: 2px;
cursor: move;
}
.list-group-item {
position: relative;
display: block;
padding: 0.75rem 1.25rem;
margin-bottom: -1px;
background-color: #fff;
border: 1px solid rgba(0, 0, 0, 0.125);
}
.flip-list-move {
transition: transform 0.5s;
}
.no-move {
transition: transform 0s;
}
.ghost {
opacity: 0.5;
background: #c8ebfb;
}
.list-group-item {
cursor: move;
}
</style>

Nuxt / Vuex / Vue Reactivity Issue Increment

Hi everyone I am I having some difficulty when working with Nuxt and Vuex.
I am trying to run through the example Vuex / Nuxt Classic Mode.
https://nuxtjs.org/guide/vuex-store/
After clicking my increment button I dont see the number go up. My page just stays at 0, I can see within the console that the state knows the number is no longer 0 but not on the screen, as if it doesnt know to be reactive.
My assumption is that I have miss configured something somewhere and my 0 is not the actual state, but I created some copy of it somehow.
Here is my button within my template.
<button #click="inc">{{ counter }}</button>
Here is my inc function within my methods.
inc () {
this.$store.commit('increment')
},
Here is my computed
computed: {
counter () {
return this.$store.getters.counter
}
}
Here is my Vuex/index.js file contained within the store folder.
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const createStore = () => {
return new Vuex.Store({
state: () => ({
counter: 0
}),
getters: {
counter: state => state.counter
},
mutations: {
increment (state) {
state.counter++
}
}
})
}
export default createStore
Update: Included more code snippets, and replied to existing comments.
#Boussadjra Brahim #xaviert, thank you both for weighing in, I appreciate the assistance.
#Boussadjra Brahim - Yes, I had tried using an action that called the mutation, that didnt seem to get me there either. I also tried adjusting the state via the action alone, and wasnt able to make any changes, however that seems correct, as I am under the impression that actions call mutations to make state changes and do not themselves do so, please correct me if you know more. I am 100% open to the idea that I did not attempt it correctly. Below is that action that didnt do anything and the one that called the mutation
actions: {
increment (state) {
state.counter++
}
},
And here is the version with the action calling the mutation.
actions: {
incrementCounterUp () {
this.commit('increment')
}
},
#xaviert - I have tried starting the server over, and have also tried to see if an nuxt build followed by a firebase serve, to see if maybe that helped. It did not. My normal server start is 'npm run dev'. In hopes that you/anyone else may be able to find my mistake below is my full _id.vue component and also my nuxt.config.js file as maybe that's it. Its still pretty raw and could use a lot of refactoring so hope you can sort through it well enough.
_.id.vue
<template>
<div class="product">
<div class="product-image">
<div class="product-image-img">
<img v-bind:src="product.image_file" width="450px;"/>
</div>
</div>
<div class="product-details-wrapper">
<div class="product-details">
<h1>
<div v-if="this.$route.query.editPage">
<textarea #input="updateTextArea" ref="textarea2" v-model="product.item_name" type="text" />
</div>
<div v-else>{{product.item_name}}</div>
</h1>
<div class="product-description">
<div class="product-description-text" v-if="this.$route.query.editPage">
<textarea #input="updateTextArea" ref="textarea" v-model="product.description" type="text" />
</div>
<div class="product-description-text" v-else v-html="product.description"></div>
</div>
<p class="product-brand"><strong>Brand - </strong> {{product.brand_name}}</p>
<hr />
<div class="product-price">
<div v-if="this.$route.query.editPage">
<strong>Original Price - </strong>
<input v-model="product.msrp" type="text" />
</div>
<div v-else class="product-msrp">
<strong>Original Price - </strong>
<span class="strike">${{product.msrp}}</span>
</div>
<div v-if="this.$route.query.editPage">
<strong>Sale Price - </strong>
<input v-model="product.price" type="text" />
</div>
<div v-else class="product-sale-price">
<strong>Sale Price - </strong>
<span class="">${{product.price}}</span>
</div>
<div class="product-price">
Quantity x
<input #input="updateQuantity" v-model="quantity" min="1" class="" type="number" value="1" />
</div>
<button #click="inc">{{ counter }}</button>
</div>
</div>
</div>
<div v-if="this.$route.query.editPage" class="update-product"> <button #click="updateProduct(product)">Update</button></div>
<div class="footer">
<router-link to="/privacy-policy" target="_blank">Privacy</router-link> |
<router-link to="/terms" target="_blank">Terms</router-link>
</div>
</div>
</template>
<script>
// # is an alias to /src
import firebase from '#/services/fireinit'
import foo from '#/components/foo'
const db = firebase.firestore()
export default {
name: 'ProductPage',
head () {
return {
title: this.product.item_name
}
},
components: {
foo
},
data: function () {
return {
product: {},
image: '',
name: 'Checkout',
description: '',
currency: 'USD',
amount: '',
msrp: '',
quantity: 1
}
},
methods: {
inc () {
this.$store.dispatch('incrementCounterUp', true)
},
updateProduct: function (product) {
db.collection('products').doc(product.item_id).set(product)
.then(function () {
console.log('Document successfully written!')
})
.catch(function (error) {
console.error('Error writing document: ', error)
})
},
updateQuantity () {
this.product.msrp = (this.quantity * this.product.orgMsrp)
this.product.msrp = Math.round(100 * this.product.msrp) / 100
this.product.price = this.quantity * this.product.orgPrice
this.product.price = Math.round(100 * this.product.price) / 100
},
updateTextArea () {
this.$refs.textarea.style.minHeight = this.$refs.textarea.scrollHeight + 'px'
this.$refs.textarea2.style.minHeight = this.$refs.textarea2.scrollHeight + 'px'
}
},
async asyncData({app, params, error}) {
const ref = db.collection("products").doc(params.id)
let snap
let thisProduct = {}
try {
snap = await ref.get()
thisProduct = snap.data()
thisProduct.orgMsrp = thisProduct.msrp
thisProduct.orgPrice = thisProduct.price
} catch (e) {
// TODO: error handling
console.error(e)
}
return {
product: thisProduct
}
},
mounted () {
if(this.$refs.textarea) {
this.$refs.textarea.style.minHeight = this.$refs.textarea.scrollHeight + 'px'
this.$refs.textarea2.style.minHeight = this.$refs.textarea2.scrollHeight + 'px'
}
},
computed: {
counter () {
return this.$store.getters.counter
}
}
}
</script>
<style lang="less">
body {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
margin: 0
}
p{
margin-top: 1em;
margin-bottom: 1em;
}
html, body, #__nuxt, #__layout, .default, .product{
height: 100%;
}
.product {
justify-content: center;
display: flex;
max-width: 1150px;
margin: 0 auto;
flex-wrap: wrap;
align-items: center;
padding: 0 24px;
&-price{
input {
border: 1px solid;
padding: 0 .3em;
text-align: center;
width: 50px;
}
}
}
.product-details{
width: 100%;
textarea {
width: 100%;
font-size: inherit;
color: inherit;
font-family: inherit;
font-weight: inherit;
height: initial;
resize: none;
background-color: transparent;
border: none;
}
h1{
font-size: 1.9rem;
margin: 15px 0 20px;
}
hr{
width: 50%;
margin: .5rem 0px;
}
p{
}
}
.product-description-text{
margin: 10px 0;
}
.product-image, .product-details-wrapper{
align-items: center;
display: flex;
justify-content: center;
}
.product-details-wrapper{
flex: 0 1 535px;
}
.product-image{
flex: 0 1 535px;
img{
width: 100%;
}
}
.product-price{
.strike{
text-decoration: line-through;
}
button{
display: flex;
width: 150px;
height: 50px;
border-radius: 5px;
justify-content: center;
font-size: 24px;
margin-top: 20px;
&:hover{
cursor: pointer;
background-color: #f1f1f1;
box-shadow: 3px 3px 11px -1px rgba(0, 0, 0, 0.48);
}
}
}
.product-sale-price{
color: #f30000;
}
.footer {
flex: 1 1 100%;
text-align: center;
color: #ccc;
margin-top: 25px;
padding: 15px;
a {
color: #ccc;
text-decoration: none;
&:hover{
text-decoration: underline;
}
}
}
.update-product{
position: absolute;
top: 0;
text-align: center;
}
</style>
nuxt.confgs.js
const pkg = require('./package')
const { STRIPE_TOKEN } = process.env;
module.exports = {
vue: {
config: {
productionTip: false,
devtools: true
}
},
buildDir: './functions/nuxt',
mode: 'universal',
/*
** 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' }
]
},
/*
** Customize the progress-bar color
*/
loading: { color: '#fff' },
/*
** Global CSS
*/
css: [
],
/*
** Plugins to load before mounting the App
*/
/*
** Nuxt.js modules
*/
modules: [
// Doc: https://github.com/nuxt-community/axios-module#usage
'#nuxtjs/axios',
'nuxt-stripe-module'
],
stripe: {
version: 'v3',
publishableKey: 'pk_test_XXX',
},
/*
** Axios module configuration
*/
axios: {
// See https://github.com/nuxt-community/axios-module#options
},
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
publicPath: '/public/',
vendor: [],
extractCSS: true,
bable: {
presets: [
'es2015',
'stage-8'
],
plugins: [
['transform-runtime', {
'polyfill': true,
'regenerator': true
}]
]
},
extend (config, { isDev }) {
if (isDev && process.client) {
config.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /(node_modules)/
})
}
},
router: {
middleware: 'router-auth'
}
},
plugins: [
{ src: '~/plugins/fireauth', ssr: true }
]
}
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const createStore = () => {
return new Vuex.Store({
state: () => ({
counter: 0
}),
actions: {
incrementCounterUp () {
this.commit('increment')
}
},
getters: {
counter: state => state.counter
},
mutations: {
increment (state) {
state.counter++
}
}
})
}
export default createStore
At the end of the day, I was not able to identify exactly what in my application was the error.
I assume that my during my initialization, configuration or development I touched something that should not have been touched, installed something that should not have be installed, messed with a package I should not have or was to bold in my nuxt.config.js changes.
I created a new nuxt app following the same install instructions. https://nuxtjs.org/guide/installation/
Moved the above _id.vue component exactly as it is and once I got dependencies updated it worked perfectly as seen in the image below.
Thank you very much #Boussadjra Brahim, #xaviert, #Andrew1325 for you assistance.

Unexpected behaviour removing a child component (row)

Description:
I have a table with some products, each row is a custom vue <row> component.
Each element has a closing (removing) button that triggers the custom "remove" event. The main app listens to this event and removes the children (by index)
The row a part from some static text it contains an input with a number.
The problem:
The parent (Vue app) removes the row, but the value of the input is then moved (and replaces its previous value) to the input in the next row.
Expected behaviour:
I want to simply remove the item I do not care about the value of the text input once it's removed. It should not move its value to the next sibling.
I attach an example.
let row = Vue.component('row', {
name: 'row',
props: ['number', 'name', 'sq'],
data: () => ({
quantity: 0
}),
template: '<tr>' +
'<td>{{number}}</td>' +
'<td>{{name}}</td>' +
'<td><button v-on:click="quantity--">-</button><input type="text" :value="quantity"><button v-on:click="quantity++">+</button></td>' +
'<td><button v-on:click="remove">×</button></td>' +
'</tr>',
methods: {
remove: function() {
this.$emit('remove', this.quantity)
}
},
beforeMount() {
this.quantity = this.sq
}
})
new Vue({
el: "#app",
data: {
out: [],
rows: [{
name: "Icecream",
sq: 0
},
{
name: "Sugar cube",
sq: 50
},
{
name: "Peanut butter",
sq: 0
},
{
name: "Heavy cream",
sq: 0
},
{
name: "Cramberry juice",
sq: 0
}
]
},
methods: {
removeRow: function(index, quantity) {
this.out.push(`Removing row ${index} (${this.rows[index].name} | ${quantity} units)`)
this.rows.splice(index, 1)
}
},
computed: {
log: function() {
return this.out.join("\r\n")
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
h2 {
font-weight: bold;
margin-bottom: 10px;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
td {
padding: 4px 5px;
}
input {
width: 40px;
text-align: center;
}
h4 {
margin-top: 20px;
margin-bottom: 5px;
}
#log {
padding: 10px;
background: #20262E;
color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<h2>Cart:</h2>
<table>
<row v-for="(row, index) in rows" :number="index" :name="row.name" :sq="row.sq" v-on:remove="removeRow(index, $event)"></row>
</table>
<h4>Log</h4>
<pre id="log" v-html="log"></pre>
</div>
As #Bert mentioned in the comments.
The problem was that I was missing a key.
https://v2.vuejs.org/v2/api/#key
Adding it solved the problem
Thanks