VueJs v-for loop not updating correctly - javascript

Im trying to make a form where you have buttons to add children inputs but after adding one child the button no longer works and it returns to error in the cole as to why but if i output the data it looks like it is being adding just the DOM isn't being updated with the new input box
var default_item = {
value: 0,
reps: 0,
sets: 0
}
var deafult_exercise = {
exercise_name: '',
item_data: [default_item]
};
new Vue({
el: '#app',
data: {
log_data: [
{
log_name: 'log #1',
log_day: 1,
exercise_data: [
{
exercise_name: 'exercise #1',
item_data: [
{
value: 50,
reps: 5,
sets: 5
}
]
}
]
}
]
},
methods: {
addLog: function(){
this.log_data.push({log_name: '', log_day:0, exercise_data: deafult_exercise});
},
addExercise: function(index_id) {
this.log_data[index_id].exercise_data.push(deafult_exercise);
},
addItem: function(index_id, log_id) {
this.log_data[log_id].exercise_data[index_id].item_data.push(default_item);
}
}
})
JSfiddle: https://jsfiddle.net/renlok/5mpace1q/3/

Your issue is that you are pushing the same items and referencing the same items so essentially you are only ever adding 1 item
You need to use
Object.assign({}, default_item)
To use new instances of the items see updated fiddle https://jsfiddle.net/5mpace1q/4/

Related

Loop array value into object javascript

i have problem with my javascript project.
i have an array with value like this :
exmpArr = ["PX1","PX2","PX3"];
and i want to loop and push it to obj like this:
sections = [
{
rows: [
{ title: exmpArr[i], rowId: exmpArr[i] },
],
},
];
the final value must like this:
sections = [
{
rows: [
{ title: "PX1", rowId: "PX1" },
{ title: "PX2", rowId: "PX2" },
{ title: "PX3", rowId: "PX3" },
],
},
];
what should i do?
what i did is i put for loop inside the object and its not work
map returns a new array from the one you're mapping over. So you can immediately assign that array as the property value when you build your object.
const exmpArr = ['PX1', 'PX2', 'PX3'];
const sections = [
{
rows: exmpArr.map(el => {
return { title: el, rowId: el };
})
}
];
console.log(sections);

Pug hasn't access to computed properties in vue inside dynamic class attribute

So, i tried to dynamically toggle className, based on computed property, but it looks like pug doesn't have access to computed properties. I tried to manually set true to a className, then it's working.
I tried to reassign computed property to pug variable, but it still doesn't work
When using pure html, classes dynamically toggle correctly
Pug:
main#app.container
- var isPinkDefinitely = isPink ? 'pink' : 'gray';
div.section(
v-bind:class=[
'custom-section',
{
'custom-section--pink': isPink
}
]
v-bind:style=[
{
'background-color': isPinkDefinitely
}
]
) {{ isPink }}
form(#submit.prevent="addItem")
label.label Add another
div.field.has-addons
div.control
input.input(required, autofocus, v-model="newItem", placeholder="Remake this in React")
button(type="submit").button.is-info
i.fa.fa-plus
span Add
transition(name="slide")
div(v-show="items.length > 0")
hr
ul
transition-group(name="slide")
li(v-for="(item, index) in items", :key="item.id")
button(#click="removeItem(index)").button.is-danger
i.fa.fa-trash
span(v-text="item.desc")
hr
span(v-text="'Total: ' + items.length")
JS:
new Vue({
el: '#app',
data: {
items: [
{id: 1, desc: "Lorem"},
{id: 2, desc: "Ipsum"},
{id: 3, desc: "Dolor"},
],
newItem: '',
},
computed: {
isPink() {
return true;
}
},
methods: {
addItem () {
const id = this.items.length + 1
this.items.push({id, desc: this.newItem})
this.newItem = ''
},
removeItem (index) {
this.items.splice(index, 1)
},
},
})
https://codepen.io/itprogressuz/pen/qBoePob?editors=1010
UPD:
SO the basically solution was to just write all classes in one line inside just an object. see solution of #aykut
I just tried to solve your problem and I think i successed. You could use variable like me. If you want change it in computed function, it will change dynamically. You could also change it in methods functions when get users events. Here, my solution.
main#app.container
div.section(
class="default-style"
:class="{'bg-pink': isPink }"
) {{ setIsPink }}
form(#submit.prevent="addItem")
label.label Add another
div.field.has-addons
div.control
input.input(required, autofocus, v-model="newItem", placeholder="Remake
this in React")
button(type="submit").button.is-info
i.fa.fa-plus
span Add
transition(name="slide")
div(v-show="items.length > 0")
hr
ul
transition-group(name="slide")
li(v-for="(item, index) in items", :key="item.id")
button(#click="removeItem(index)").button.is-danger
i.fa.fa-trash
span(v-text="item.desc")
hr
span(v-text="'Total: ' + items.length")
// css file
.default-style{
background-color: gray;
}
.bg-pink{
background-color: pink;
}
// js file
new Vue({
el: '#app',
data: {
isPink: false,
items: [
{id: 1, desc: "Lorem"},
{id: 2, desc: "Ipsum"},
{id: 3, desc: "Dolor"},
],
newItem: '',
},
computed: {
setIsPink() {
this.isPink = !this.isPink;
return this.isPink;
}
},
methods: {
addItem () {
const id = this.items.length + 1
this.items.push({id, desc: this.newItem})
this.newItem = ''
},
removeItem (index) {
this.items.splice(index, 1)
},
},
})

React update State Array inside of Array

I'm currently try to update the state object inside of my react class based component.
The problem is that my state is an 2 dimensional array which looks like this:
this.state = {
items: [
{
title: 'first depth',
items: [
{
title: 'second depth',
},
],
},
],
};
For updating the first depth of the array inside my state object I use this code:
this.setState(({items}) => ({
items: [
...items.slice(0, index),
{
...items[index],
title: 'new Title',
},
...items.slice(index + 1),
],
}));
But I cannot update the second depth of the array inside my state.
Does someone has an idea how to update the second depth?
create new state by spread, then change inner array by splice:
this.setState((oldState) => {
const newState = {...oldState};
const newItem = { ...newState.items[index], title:"new Title"};
newState.items.splice(index, 1, newItem);
return newState;
}
Following the same pattern, you will need the index of the 2nd depth item you want to change.
this.setState(({items}) => ({
items: [
...items.slice(0, index),
{
items: [
...items[index].slice(0, indexOf2ndDepth),
{
title: 'new Title'
},
...items[index].slice(indexOf2ndDepth + 1),
],
title: 'new Title',
},
...items.slice(index + 1),
],
};
}));
This can get pretty complex and I recommended you isolate the 2nd depth array first, make changes, and insert it into the 1st depth array
this.setState(({ items }) => {
const secondDepthArray = [
...items[index].slice(0, indexOf2ndDepth),
{
title: 'new Title',
},
...items[index].slice(indexOf2ndDepth + 1),
];
return {
items: [
...items.slice(0, index),
{
items: secondDepthArray
title: 'new Title',
},
...items.slice(index + 1),
],
};
});
To do that you can store your state in variable and update your array after that use prev prevState to update your State your array
You can clone new array from state
const newItems = [...this.state.items]
// And then modify it
So if you want to update array just setState
this.setState({items: newItems})

NaN issues on my sum of values in my array of objects

i have been building this APP in VUE.js , but have a little issue on one function where in im trying to add the values of each object inside the array..throwing me a NaN error.Lets say
script
data(){
return{
array:[
{ product_name: "Chain Saw",
product_free: 2,
product_free_discount: 306.8,
product_id: 1,
}
{ product_name: "Ox",
product_free: 1,
product_free_discount: 60.8,
product_id: 1,
}
],
totalDiscEquals:0,
}
}
then on my computed property i just set this function
COMPUTED
totalDiscObUnique() {
let total = this.array.reduce(
(a, b) => a + b.product_free_discount,
0
);
console.log(total);
return this.totalDiscEquals=total;
},
creating the process in the created module like this
created(){
this.totalDiscObUnique;
but the when i console log the value of totalDisEquals, throws me a NaN result , any idea about why is this happening, or sugerences about why NaN mostly times ocurr for?
new Vue({
el: '#app',
data: {
array:[
{
product_name: "Chain Saw",
product_free: 2,
product_free_discount: 306.8,
product_id: 1,
}, // <--- [1] missing comma here (,)
{
product_name: "Ox",
product_free: 1,
product_free_discount: 60.8,
product_id: 1,
}
],
totalDiscEquals:0,
},
/* [2] It's not recommended to assign new value to a data property
in a computed, instead it intended to give you just a filtered / reduced
/ ... response. */
computed: {
totalDiscObUnique() {
return this.array.reduce((a, b) => a + b.product_free_discount, 0);
},
},
created () {
/* [3] also, there is no need to reassign the same value to a new
data property again, instead you can use the computed itself wherever you
want to use it */
// this.totalDiscEquals = this.totalDiscObUnique
console.log(this.totalDiscObUnique)
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
{{ totalDiscObUnique }}
</div>

How to traverse a list of items in VueJS

Here is my vue instance:
new Vue({
el: '#app',
data: {
showPerson: true,
persons:
[
{id: 1, name: 'Alex'},
{id: 2, name: 'Bob'},
{id: 3, name: 'Chris'}
],
},
methods: {
nextPerson: function(){
this.showPerson = false;
}
}
});
I am trying to walk the persons array of objects. I want the list to start with the first element of the array and below it should be a button which is responsible for hiding the previous element and showing the next element of the array. Once the user reaches the last element, the Next button should not go back to the first element.
Here is the HTML:
<div id="app">
<ul v-for="person in persons">
<li v-if="showPerson">{{person.name}}</li>
</ul>
<button #click="nextPerson">Next Person</button>
</div>
And the JSBin Link. At this moment I can only show and hide the items all at once and not one at a time. How can I implement this?
One of the ways of doing so would be to keep an index for the person being shown on screen. I've named this variable as shownPersonIndex.
Then, you need to show the next person on click of button. So in the click event handler, you need to increment the index by 1. Also, you need to ensure that the index value does not exceed the length of the array. So I've modified the click handler as follows:
nextPerson: function() {
if(this.shownPersonIndex < (this.persons.length - 1)) {
this.shownPersonIndex++;
}
}
Finally, you can either use a computed to display the currently shown person or an inline expression like this.persons[this.shownPersonIndex].name to show the person on screen.
I am using v-if="this.shownPersonIndex != this.persons.length - 1" to hide the "next" button as you reach the last element on the array.
new Vue({
el: '#app',
data: {
shownPersonIndex: 0,
persons: [{
id: 1,
name: 'Alex'
},
{
id: 2,
name: 'Bob'
},
{
id: 3,
name: 'Chris'
}
],
},
methods: {
nextPerson: function() {
if(this.shownPersonIndex < (this.persons.length - 1)) {
this.shownPersonIndex++;
}
}
},
computed: {
shownPerson: function() {
return this.persons[this.shownPersonIndex];
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="app">
Person: {{ shownPerson.name }}
<button v-if="this.shownPersonIndex != this.persons.length - 1" #click="nextPerson">Next Person</button>
</div>

Categories

Resources