Vue pass data into array specific item name - javascript

Hey I am really new to Vue and for this project I was trying to add data inside array using .push() . When I push data inside totalPlayers its suppose to get data as totalPlayers[{data:[0,1,2,3]}] but it currently saves data as totalPlayers[{data: [] }, 0, 1, 2, 3]. Is there a way to fix this? Here is my code below
JsFiddle = https://jsfiddle.net/ujjumaki/xv2homt8/24/
Method
new Vue({
el: "#app",
data: {
totalPlayers:[{
data:[],
}],
playerList:4,
},
methods: {
buttonClicked(){
for (var i = 0; i < this.playerList; i++) {
console.log('i was '+i);
this.totalPlayers.push(i);
console.log(this.totalPlayers);
}
}
}
})
View
<div id="app">
<button #click="buttonClicked()">
Click Me
</button>
</div>

data is array in first element of totalPlayers array, so try totalPlayers[0].data.push:
new Vue({
el: "#app",
data: {
totalPlayers:[{
data:[],
}],
playerList:4,
},
methods: {
buttonClicked(){
for (var i = 0; i < this.playerList; i++) {
console.log('i was '+i);
this.totalPlayers[0].data.push(i);
console.log(this.totalPlayers);
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="buttonClicked()">
Click Me
</button>
</div>

Just use this to push: this.totalPlayers[0].data.push(i);
since totalPlayers is an array and you want to add to it's first element that is an object
jsfiddle

Related

How to display values of an associative array with vue.js?

I have an array which is:
And I want to make a foreach loop and list all key's & script_content's to the view.
My vue components mounted method:
mounted() {
this.loading = true;
axios.get('/app/json-ld/json-ld-settings')
.then(res => {
let data = res.data;
console.log(data.scripts);
this.key = data.scripts[0]['key'];
this.scriptContent = data.scripts[0]['script_content'];
})
.catch(error => {
this.loading = false;
this.$notify({
group: 'notify',
type: 'error',
text: 'Something happened! Please refresh the page and try again or contact support!',
});
});
},
component data:
data: () => ({
errors: {},
key: [],
scriptContent: [],
I am able to display the values of the first array, but don't know how to make a foreach loop in an associative array.
HTML:
<div class="py-3 d-flex flex-row justify-content-end align-content-end">
<div class="pr-2">
<h5>Key</h5>
<span>{{key}}</span>
</div>
<div class="pl-2">
<h5>Script content</h5>
<span>{{scriptContent}}</span>
</div>
</div>
The goal is to list all key's and script_content's in a HTML list or a div.
Any help will be appriciated.
You can just use codes below:
data() {
return {
keys: [],
contents: [],
}
}
...
for (let index in data) {
this.keys.push(data[index].key);
this.contents.push(data[index].script_content);
}
...
Then you can use v-for in html codes to use keys and contents.
You should store all scripts into the data, not just data.scripts[0], and then iterate over them in the template using v-for directive. Here is a couple of good examples:
https://v2.vuejs.org/v2/guide/list.html

Deleting specific component in v-for array

I have below array, that contains a number of columns. Below example contains three columns, but columns can be added/removed dynamically:
[['position', '30'], ['position', '60'], ['position', '90']]
I am facing issues when deleting the correct column (index in array) with Vue.
Consider below snippet:
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
columns: [['position', '30'], ['position', '60'], ['position', '90']]
},
methods: {
deleteColumn: function(index) {
this.columns.splice(index, 1);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(item, index) in columns" :key="index">
Column #: {{index}} - <a #click="deleteColumn(index)">Delete me</a>
</div>
</div>
If you run the above code snippet end try to delete the #1 column, it will actually remove the #2 column (last item of the array). Same goes for #0.
I thought that by providing the index to my deleteColumn function, I could remove the "right" index from the array.
Any help is appreciated.
Just give them a property name and you are done. Notice what I changed here. Columns is no more a 2D array, but objects. Use this.$delete(this.columns, index); to delete the objects.
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
columns: {
'1': {
position: 30
},
'2': {
position: 60
},
'3': {
position: 90
}
}
},
methods: {
deleteColumn: function(index) {
this.$delete(this.columns, index);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(item, index) in columns" :key="index">
Column #: {{index}} - <a #click="deleteColumn(index)">Delete me</a>
</div>
</div>
{
'1': {
position: 30
},
'2': {
position: 60
},
'3': {
position: 90
}
}
Here, '1' is a property name and it's value is another object. It's like giving ids to your data.
The format for value of object is this
{ property_name : value }
Here, value is another object, and in that object, there is another property, named "position" with your corresponding values.
When you clicked any item you are removing it in the right way, your index is your key, that's the problem, but is visually, in the logic it's right. Display your position in your template just for you can see it. ANd for me your data it's not in the right way.
<div id="app">
<div v-for="(item, index) in columns" :key="index">
Column #: {{index}}-{{item.position}} -
<a #click="deleteColumn(index)">Delete me</a>
</div>
</div>
and your script for you can see the change
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
columns: [{position: 30}, {position: 60}, {position: 90}]
},
methods: {
deleteColumn: function(index) {
this.columns.splice(index, 1);
}
}
})
The splice method reindexes the array, moving all elements after the splice point up or down so that any new inserted values will fit and so that the array indices remain contiguous. You can see it more clearly if you also display the values of the items in your list:
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
columns: ['foo', 'bar', 'baz']
},
methods: {
deleteColumn: function(index) {
this.columns.splice(index, 1);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(item, index) in columns" :key="index">
Column #{{index}} = {{item}} - <a #click="deleteColumn(index)" style="cursor:pointer">Delete me</a>
</div>
</div>
Initially, the snippet above will render like this:
Column #0 = foo - Delete me
Column #1 = bar - Delete me
Column #2 = baz - Delete me
If you now click the "Delete me" link on column #0 ("foo"), it will change to:
Column #0 = bar - Delete me
Column #1 = baz - Delete me
You can see that the value "foo" indeed got spliced out of the array — and the values "bar" and "baz" were shifted down by one position to become the new elements #0 and #1.
Anyway, the fix for this problem is simply "don't do that":
If you're using v-for with a simple array whose elements have no natural key value, you can just omit :key entirely and let Vue decide how to best handle changes to the underlying array. As long as the contents within the v-for loop doesn't contain any form inputs or stateful components or other fancy stuff that doesn't react well to the array being reindexed, it should work just fine.
Conversely, if you do have a natural unique key available for each array element, use it. If you don't, but can create one, consider doing that.
You should not use index as the key with CRUD operations since this will confuse Vue when it comes to deleting. The key should be a unique identifier that relates to the data.
You can create a new formatted array of objects on mount with a key generated from the data within the array (note: I haven't tested the code in a browser if there are any mistakes).
<template>
<div>
<div v-for="col in formattedColumns" :key="col.key">
{{ col.value }}
</div>
</div>
</template>
<script>
export default {
data() {
return {
columns: [['position', '30'], ['position', '60'], ['position', '90']],
formattedColumns: null,
};
},
mounted() {
let columns = [];
for (let i = 0; i < this.columns.length; i++) {
columns.push({
value: this.columns[i],
key: this.columns[i][0] + this.columns[i][1],
});
}
this.formattedColumns = columns;
},
};
</script>
Try this this.$delete(this.columns, index) which is the same as Vue.delete(this.columns, index)
https://v2.vuejs.org/v2/api/index.html#Vue-delete

Vue.js v-for element from $refs broken in watch function

With Vue.js 2.5.22 and FireFox 65.0. What am I missing?
https://jsfiddle.net/r083hqgv/2/
A v-for element identified by a :ref="x" attribute doesn't work as expected in a watch function. I've also tried using :id="x" & getElementById(), and calling setTimeout(..., 200) within $nextTick().
Code from the above fiddle:
<div id="app" style="position:relative">
<h2>last element top: {{offset+''}}</h2>
<button #click="add()">Add & get top</button>
<ol>
<li v-for="a in list" :key="'r.'+a">
<a #click.stop.prevent="get($event.target)" href="#"
:ref="'r.'+a">get top {{'r.'+a}}</a>
</li>
</ol>
</div>
new Vue({
el: "#app",
data: {
offset: 0,
last: 'unset',
list: [],
},
methods: {
add: function() {
this.last = 'r.'+ this.list.push(this.list.length+1);
this.list = this.list.slice();
},
get: function(iEl) {
this.offset = iEl.offsetTop;
iEl.style = 'font-style:italic';
}
},
watch: {
list: function() {
this.$nextTick(function() {
var aEl = this.$refs[this.last];
if (aEl) this.get(aEl);
});
}
}
})
As referenced by the documentation ($refs), this.$refs["..."] returns an array when v-for is used. Therefore, change
if (aEl) this.get(aEl);
to
if (aEl) this.get(aEl[0]);
Everything will work (I already tested it on your jsfiddle).

VueJS Array Index returns wrong result

I am creating a poetry app where poetry is fetched using an API call.
I fetch data using axios library and do v-for to populate data. I use the index from v-for to populate the image for each poem respectively.
I display 10 results per page using my own custom pagination. Currently, it's only for next button though.
The problem I am facing is when I navigate to Page 2! As I said earlier, that I use v-for's index to display images, it doesn't actually update the index when I move to the next page. As a result, the images are shown same as of page 1.
Code:
new Vue({
el: '#app',
data: {
proxy: 'https://cors-anywhere.herokuapp.com/',
imageIndex: 0,
pagination: {
start: 0,
end: 10,
resPerPage: 10
},
fetchData: [],
fetchImages: []
},
methods: {
paginate() {
this.pagination.start = this.pagination.start + this.pagination.resPerPage;
this.pagination.end = this.pagination.end + this.pagination.resPerPage;
},
async fetchDatas() {
try {
const res = await axios(`${this.proxy}http://poetrydb.org/author,title/Shakespeare;Sonnet`);
if (res) {
this.fetchData = res.data;
}
} catch (error) {
console.log(error);
}
},
async fetchImagess() {
const key = '9520054-7cf775cfe7a0d903224a0f896';
const perPage = 154;
const proxy = ''
const res = await axios(`${this.proxy}https://pixabay.com/api/?key=${key}&per_page=${perPage}`);
this.fetchImages = res.data.hits;
}
},
mounted() {
this.fetchDatas();
this.fetchImagess();
}
});
<div id="app">
<div v-for="(poetry, index) in fetchData.slice(this.pagination.start, this.pagination.end)">
<div>
<img :src="fetchImages[index].largeImageURL.toLowerCase()" style="max-width: 100%;height: auto;max-height: 320px;">
<div>
<h5>{{ poetry.title }}</h5>
<span v-for="(poetryBody, i) in poetry.lines.slice(0, 5)">
{{ i === 4 ? poetryBody.split(',').join('') + '...' : poetryBody }}
</span>
<br>
Read More
</div>
</div>
</div>
<nav style="padding-top: 3em;">
<button #click="paginate()">Next</button>
</nav>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
JSFiddle: http://jsfiddle.net/sanjaybanjade/vnu654gk/9/
As you can see the images doesn't get updated when I goto Page 2! Please help me fix this!
And please ignore the console errors. I am gonna fix them later.
The quick fix would be to calculate the offset in line 4 to update on pagination:
<img v-bind:src="fetchImages[index + pagination.start].largeImageURL.toLowerCase()" style="max-width: 100%;height: auto;max-height: 320px;">
wrong at this line fetchImages[index].largeImageURL.toLowerCase().
Since you are iterating a sliced array of fetchData, it's index is related to sliced array, not original array. So, you should apply pagination slice to your fetchImages too.
When you run fetchData.slice(), it returns a new object. So if you slice out 10 new pieces of poetry, their indexes are still going to be 0-9, since the returned object only has that many items each time.
Why it's not working is because you only slice fetchData on this line fetchData.slice(this.pagination.start, this.pagination.end) but you don't slice the fetchImages what means fetchImages still is the same array it didn't change, meaning that index 0 is still the same image. Best is if you keep them in sync so I would add a pageData and pageImages array's and every time you change the paging you update both of them. like in a updatePageData method
new Vue ({
el: '#app',
data: {
proxy: 'https://cors-anywhere.herokuapp.com/',
imageIndex: 0,
pagination: {
start: 0,
end: 10,
resPerPage: 10
},
fetchData: [],
fetchImages: [],
pageData: [],
pageImages: []
},
methods: {
paginateNext() {
this.pagination.start = this.pagination.start + this.pagination.resPerPage;
this.pagination.end = this.pagination.end + this.pagination.resPerPage;
this.updatePageData()
},
updatePageData () {
this.pageData = this.fetchData.slice(this.pagination.start, this.pagination.end)
this.pageImages = this.fetchImages.slice(this.pagination.start, this.pagination.end)
},
async fetchDatas() {
try {
const res = await axios(`${this.proxy}http://poetrydb.org/author,title/Shakespeare;Sonnet`);
if(res) {
this.fetchData = res.data;
}
} catch(error) {
console.log(error);
}
},
async fetchImagess() {
const key = '9520054-7cf775cfe7a0d903224a0f896';
const perPage = 154;
const proxy = ''
const res = await axios(`${this.proxy}https://pixabay.com/api/?key=${key}&per_page=${perPage}`);
this.fetchImages = res.data.hits;
}
},
mounted() {
Promise.all([
this.fetchDatas(),
this.fetchImagess()
])
.then(() => this.updatePageData())
}
});
<div id="app">
<div v-for="(poetry, index) in pageData">
<div>
<img :src="pageImages[index].largeImageURL.toLowerCase()" style="max-width: 100%;height: auto;max-height: 320px;">
<div>
<h5>{{ poetry.title }}</h5>
<span v-for="(poetryBody, i) in poetry.lines.slice(0, 5)">
{{ i === 4 ? poetryBody.split(',').join('') + '...' : poetryBody }}
</span>
<br>
Read More
</div>
</div>
</div>
<nav style="padding-top: 3em;">
<button #click="paginateNext()">Next</button>
</nav>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>

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