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
Related
I'm trying to automate naming conventions used for a reactive dictionary, but I'm stuck not knowing how to access the reactive dictionary key based on the data attribute I fetched on click.
Expected: Use same naming convention for both ReactiveDict keys and data-attribute names on HTML elements so I can fetch the name on the element clicked, take its data-attribute name and automatically know which ReactiveDict key it is because they use the same name.
Result: It's not recognizing the naming convention when trying to set or get from the ReactiveDict because... I don't know. I'm guessing it's because when you create a ReactiveDict, you use this convention template.search.set('generic_name', data) but it won't work if I replace 'generic_name' with the data-attribute name (that doesn't include the single quotes) on click.
Please see example code below and advise if you have any questions, request for more info, or funny jokes since we're all trapped at home avoiding the COVID-19 virus :slight_smile:
home-template.html
<template name="home_template">
<section class="home-template">
<div class="content">
<div class="filter-box">
<ul>
<legend class="sui-category__title">CATEGORY 1</legend>
{{#each option in category_one}}
<li data-filter-value="{{option.value}}" data-category-name="category_one">
<div class="circle">{{formatToNumberWithComma(option.count)}}</div>
<h4>{{option.value}}</h4><input id="filter-1-{{get_this(option)}}" type="checkbox" /><label for="filter-1-{{get_this(option)}}"><i class="fa fa-check"></i></label>
</li>
{{/each}}
</ul>
<span class="more-options">+ More</span>
</div>
<div class="filter-box">
<ul>
<legend class="sui-category__title">CATEGORY 2</legend>
{{#each option in category_two}}
<li data-filter-value="{{option.value}}" data-category-name="category_two">
<div class="circle">{{formatToNumberWithComma(option.count)}}</div>
<h4>{{option.value}}</h4><input id="filter-2-{{get_this(option)}}" type="checkbox" /><label for="filter-2-{{get_this(option)}}"><i class="fa fa-check"></i></label>
</li>
{{/each}}
</ul>
<span class="more-options">+ More</span>
</div>
<div class="filter-box">
<ul>
<legend class="sui-category__title">CATEGORY 3</legend>
{{#each option in category_three}}
<li data-filter-value="{{option.value}}" data-category-name="category_three">
<div class="circle">{{formatToNumberWithComma(option.count)}}</div>
<h4>{{option.value}}</h4><input id="filter-3-{{get_this(option)}}" type="checkbox" /><label for="filter-3-{{get_this(option)}}"><i class="fa fa-check"></i></label>
</li>
{{/each}}
</ul>
<span class="more-options">+ More</span>
</div>
</div>
</section>
</template>
home-template.js
Template.home_template.onCreated(() => {
const template = Template.instance();
template.search = new ReactiveDict();
template.search.set('category_one');
template.search.set('category_two');
template.search.set('category_three');
template.search.set('filter_parameters');
});
Template.home_template.helpers({
formatToNumberWithComma(number) {
return format_w_comma(number);
},
category_one() {
const template = Template.instance();
return template.search.get('category_one')[0].data;
},
category_two() {
const template = Template.instance();
return template.search.get('category_two')[0].data;
},
category_three() {
const template = Template.instance();
return template.search.get('category_three')[0].data;
},
get_this(option) {
const query_to_array = [];
const search_query_word_array = option.value.split(' ');
const search_query_word_array_count = search_query_word_array.length;
for (let i = 0; i < search_query_word_array_count; i++) {
query_to_array[i] = `${search_query_word_array[i]}`;
}
const search_query = query_to_array.join('-');
return search_query;
},
});
Template.home_template.events({
'click .home-template label': function (event, template) {
$(event.currentTarget).parent('li').toggleClass('active');
const category_clicked = $(event.currentTarget).parent('li')[0].dataset.categoryName;
const filter_selected = $(event.currentTarget).parent('li')[0].dataset.filterValue;
const array = template.search.get(category_clicked);
array.indexOf(filter_selected) === -1 ? array.push(filter_selected) : array.splice(array.indexOf(filter_selected), 1);
template.search.set(category_clicked, array);
const filter_parameters = {
filters: {
all: [{
category_one: template.search.get('category_one'),
},
{
category_two: template.search.get('category_two'),
},
{
category_three: template.search.get('category_three'),
},
],
},
};
template.search.set('filter_parameters', filter_parameters);
},
});
Thank you!
You data keys are working, you actually set the data here:
const category_clicked = $(event.currentTarget).parent('li')[0].dataset.categoryName
const filter_selected = $(event.currentTarget).parent('li')[0].dataset.filterValue
const array = template.search.get(category_clicked)
array.indexOf(filter_selected) === -1
? array.push(filter_selected)
: array.splice(array.indexOf(filter_selected), 1)
template.search.set(category_clicked, array)
Consider the following data:
template.search.set('category_one', [
{
data: [
{
value: 'some value here',
count: 100
}
]
}
])
and category_clicked to be category_one and filter_selected to be some value here then the code above will result in the following data:
[
{
data: [
{
value: 'some value here',
count: 100
}
]
},
'some value here'
]
I am not sure how you further process this data but this shows your reactive data is working. The data updated in the helper right after your clicked the label.
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).
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))
I'm beginner on Vue js. I'm facing an issue where I have to update two values while rendering.
<ul class="category-list">
<li v-for="category in categories">
<a v-bind:href="location/category.slug/all" v-text="category.name"></a>
</li>
</ul>
My javascript file
new Vue({
el : '#example',
data : {
location: 'new-york',
categories: [],
},
mounted() {
axios.get('parent-categories').then((response) => {
this.categories = response.data;
});
},
});
Ajax Response
[{"name":"Default Category","slug":"default-category"},{"name":"Main Category","slug":"main-category"}]
Here I wanted to build url structure like location/category-slug/all ie,
http://myapp.com/new-york/default-category/all
How to achieve this?
Found out how to handle this situation after a long struggle. All I have to do is to create a method that generates URL for me.
JS
new Vue({
el : '#example',
data : {
location: 'new-york',
categories: [],
},
methods: {
slug: function (category) {
return this.location+'/'+category.slug+'/all';
}
},
mounted() {
axios.get('parent-categories').then((response) => {
this.categories = response.data;
});
},
});
Html
<div id="example">
<h3>Services</h3>
<ul class="category-list">
<li v-for="category in categories">
<a :href="slug(category)" v-text="category.name"></a>
</li>
</ul>
</div>
I have list of links:
<ul id="menu">
<li v-for="item in items">
<a href v-bind:href=link>{{item.message}}</a>
</li>
</ul>
var example1 = new Vue({
el: '#menu',
data: {
items: [
{ message: 'Link1' },
{ message: 'Link2' }
]
},
computed: {
link: function() {
return f($index) // how do I access current array index ??
}
}
})
I can achieve the desired result by using mustache markup in a href attribute but it must be possible to access this $index variable from within a computed function?
You can't.
This is what methods are for:
<a href v-bind:href="link($index)">{{item.message}}</a>
methods: {
link: function(index) {
return f(index)
}
}