Append new Item to a list in Vue 2 - javascript

I have a very simple form which gets name and age from user and add this to a list.
You can check that on JSFiddle.
Here is HTML :
<div id="app">
<ul>
<li v-for="user in users">{{ user.name +' '+ user.age }}</li>
</ul>
<hr>
<form v-on:submit.prevent="addNewUser">
<input v-model="user.name" />
<input v-model="user.age" />
<button type="submit">Add User</button>
</form>
</div>
Here is Vue code:
new Vue({
el: '#app',
data: {
users: [{name:"Mack", age:20}],
user: {name:"", age:0}
},
methods: {
addNewUser() {
this.users.push(this.user);
}
}
});
The Problem
The problem is where I trying to add more than one user to the list. As you can see, when I type new value for the new user, previous values of recently added users change too!
How can I fix it?

When you push this.user to the array you're pushing the reference to that data. Instead, create a new object using the data from the user object:
this.users.push({ name: this.user.name, age: this.user.age })

You should be creating a new object which should be decoupled from the data.
new Vue({
el: '#app',
data: {
users: [{name:"Mack", age:20}],
user: {name:"", age:0}
},
methods: {
addNewUser() {
let newUser = {
name: this.user.name,
age: this.user.age
};
this.users.push(newUser);
}
}
});
One more thing I'd suggest; I see that you are interpolating the value of the name and age by writing {{ user.name +' '+ user.age }} while instead you could convert that into a computed property and make it more reusable.

If you're using lodash, try this.
this.users.push(_.cloneDeep(this.user))

Related

How to Store Data Property Value from a Specific Item in Rendered List in Vue

I'm trying create a follow button on list items in Vue. My strategy is to grab the value of a particular list item property and store it in the data object. Then use this value in a method to add it to an array in my database.
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock">+follow</button>
</div>
I'm not sure how to get the value of result.symbol "into" the button element to set the value symbol in the data object below.
<script>
export default {
data() {
return {
results: [ // this is populated by an api call
{
currency: "USD"
exchangeShortName: "NYSE"
name: "International Game Technology PLC"
stockExchange: "NYSE"
symbol: "IGT"
},
{...},
...
],
symbol: "",
};
},
followStock() {
// add this.symbol to database array
},
},
};
</script>
I'm guessing there might be an easier strategy I'm overlooking as I'm still new to Vue, so any other solution that essentially allows me to fire off the value of result.symbol from any rendered result to my database would be awesome.
You can just pass the result as a parameter to your method.
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock(result)">+follow</button>
</div>
And in your method:
methods: {
followStock(result) {
// do something with result
console.log({result});
let symbol = result.symbol;
},
}
P.S I didn't see you put your followStock() inside a methods object, but I did so in the example. https://v2.vuejs.org/v2/api/#methods
Write directly as a function call.
The vue compiler will turn followStock(result.symbol) into function(event) {followStock(result.symbol)}.
new Vue({
el: '#app',
data() {
return {
results: [
{
name: "International Game Technology PLC",
symbol: "IGT"
},
{
name: "A name",
symbol: "A symbol"
}
]
};
},
methods: {
followStock(symbol) {
console.log(symbol)
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock(result.symbol)">+follow</button>
</div>
</div>
As Nazaire mentioned you can access the results anywhere inside the child elements when using v-for.
(it works like a normal for-loop)
It's not only limited to the corresponding element (the element in which you do v-for)
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock(result.symbol)">+follow</button>
</div>
followStock(symbol){
// you can now add symbol to db
}

Vue, get value and name of select option

I'm using html and laravel to build select box options in a foreach loop
This works and populates with ID as value and name as the option. What I want, and can't quite figure out here, is how to take my function when I call it and get the id and value as separate vue options for a post axios call I'm going to make.
So when I select the option and submit the form to call the function, how can I get the ID as one prop and the name as another?
<select>
#foreach($details as $detail)
<option value="{{$detail->id}}">{{$detail->name}}</option>
#endforeach
</select>
new Vue({
data: {
detailID: [''],
detailName: ['']
},
methods: {
let data = {
detailID: this.detailID,
detailName: this.detailName
};
}
})
Here is a code example just using vue.js
Template
<div id="app">
<select v-model="selectedDetailId">
<option v-for="detail in details" v-bind:value="detail.id">
{{ detail.name }}
</option>
</select>
</div>
Script
new Vue({
el: '#app',
data: {
selectedDetailId: null,
details: [
{ id: 1, name: 'A' },
{ id: 2, name: 'B' },
{ id: 3, name: 'C' }
]
},
methods:{
post(){
//your selected Id is this.selectedDetailId
}
}
})
You can find more details and examples in the official Vue.js docs.
https://v2.vuejs.org/v2/guide/forms.html#Value-Bindings
Use v-model to bind the selection to your component data. SeeForm input bindings:
new Vue({
data: {
detailID: ""
},
// now you can access the id with this.detailID in your post request
})
<select v-model="detailID">
#foreach($details as $detail)
<option value="{{$detail->id}}">{{$detail->name}}</option>
#endforeach
</select>
And if you need both id and name, one work around could be:
new Vue({
data: {
detail: ""
},
// now you can access the id & name with this.detail.split(',') in your post request
})
<select v-model="detail">
#foreach($details as $detail)
<option value="{{$detail->id.','.$detail->name}}">{{$detail->name}}</option>
#endforeach
</select>
You would need to set a v-model to your select element and bind the entire object to each option element instead of just the id like you are doing in your example.
Here is a codepen with a simple example.

Pass click action using array and v-for (Vue.js)

I am trying to pass through a v-for the text and the #click action of each li. For the text I know how to do it...but for the click action?enter code here
Each item of the array menuOptions (which is in the 'data' part of my Vue component) is structured like this :
{name: "firstOption",action: "console.log('first option called')"}
The first parameter is the name of the option, the
<ul>
<li v-for="option in menuOptions" #click="option.action">{{option.name}}</li>
</ul>
Do you have some ideas? (I guess that's maybe a pure JS question, but maybe there are possibilities to do it Vue too?)
Pass a function expression to the action property, instead of a "stringified" function.
{
name: 'firstOption',
action: function() {console.log('first option called'}
}
var app = new Vue({
el: '#app',
data: {
menuOptions: [{
action: function() {
console.log('foo')
},
name: 'foo'
}, {
action: function() {
console.log('bar')
},
name: 'bar'
}],
}
})
<script src="https://unpkg.com/vue"></script>
<div id='app'>
<ul>
<li v-for="option in menuOptions" #click="option.action">{{option.name}}</li>
</ul>
</div>

Vue add existing Firebase ID for "relation"

I am using Vue and Firebase via VueFire to build a form to relate objects. I have the following form and method:
<form #submit.prevent="addRelation">
<select v-model="newRelation.parent">
<option v-for="item in content">{{item.name}}</option>
</select>
<select v-model="newRelation.child">
<option v-for="item in content">{{item.name}}</option>
</select>
<input type="submit" value="Add Relation"/>
</form>
And Vue:
new Vue({
el: "#app",
data: {
related: {},
newRelation: {
parent: '',
child: ''
}
},
firebase: {
content: contentRef,
related: relatedRef
},
methods: {
addRelation() {
relatedRef.push(this.newRelation)
console.log('relation added')
}
},
This adds JSON like so in Firebase:
{
"child": "objectc",
"parent": "objecta",
".key": "-KjFYZn8dqP29uLmJBJP"
},
It pushes a new Firebase ID and nests the data. In order for Firebase to handle 'relationships' correctly they must be structured like so:
{
".key":"-Existing ID of object A
"-Existing ID of object B": true
}
How can I update my method to structure info like so? I have this CodePen The bottom two entries in the "'Related' Content" section show how I want it to submit (I added manually).

v-for through an object in vue.js

I have a Teams object, which has a record of all teams and can also count and create them. I store all teams as an object, so I can write Teams.all["Team 1"] to select a team with a name of "Team 1".
Teams.js
var Teams = {
all: {},
create: function(teamName) {
var key = 'Team ' + this.count();
this.all[key] = {
'name': teamName,
'score': 0
};
},
count: function() {
var total = 0;
for(var team in this.all){
if(this.all.hasOwnProperty(team)){
total ++;
}
}
return total;
}
}
Now in vue I'd like to loop through this.
main.js
var vueApp = new Vue({
el: '#app',
data: {
'Teams' : Teams
},
methods : {
createTeam : function(){
Teams.create('Team ' + (Teams.count() + 1));
}
}
});
And then this doesn't work (obviously):
index.html
<div id="app">
<ul>
<li v-for="team in Teams.all">{{ team.name }}</li>
</ul>
<button #click="createTeam">Create team</button>
</div>
So my next guess was to go like this:
index.html
<div id="app">
<ul>
<li v-for="team in Teams.all">{{ Teams.all[team].name }}</li>
</ul>
<button #click="createTeam">Create team</button>
</div>
But that doesn't work either. Is there a way to loop through the properties of an object in Vue?
http://codepen.io/EightArmsHQ/pen/wzPKxA
Your Teams state is not reactive because you are adding object keys to it... Read this docs here: http://rc.vuejs.org/guide/reactivity.html#Change-Detection-Caveats.
Use this.$set(this.someObject, 'b', 2) if you want to add properties to your state object or those won't be reactive and trigger view update.
Also not sure why you complicate so much :). Try this:
var vueApp = new Vue({
el: '#app',
data: {
teams: []
},
methods: {
addTeam: function() {
this.teams.push({
name: 'Team ' + this.teams.length,
score: 0
})
}
}
});
<div id="app">
<ul>
<li v-for="team in teams">
{{ team.name }}
</li>
</ul>
<button #click="addTeam">Create team</button>
</div>
Demo here: http://codepen.io/anon/pen/qaVbym

Categories

Resources